Vitron-Front/app/hooks/useWebSocket.ts
2026-04-29 01:44:16 +03:30

184 lines
4.8 KiB
TypeScript

import { useEffect, useState } from "react";
import { useAuthToken } from "./use-root-data";
import { getSocketBaseUrl } from "~/utils/api-config";
const useWebSocket = (threadId: string, userId: string | undefined) => {
const [socket, setSocket] = useState<WebSocket | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [newMessage, setNewMessage] = useState<NewMessageEvent | null>(null);
const [readReceipt, setReadReceipt] = useState<ReadReceiptEvent | null>(null);
const [messageDeleted, setMessageDeleted] =
useState<MessageDeletedEvent | null>(null);
const token = useAuthToken();
const baseSocketPath = getSocketBaseUrl();
// Initialize WebSocket connection
useEffect(() => {
if (!threadId || !userId || !token) return;
// Use wss in production
const wsUrl = `${baseSocketPath}/ws/chat/${threadId}/?token=${token}`;
const ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log("WebSocket connection established");
setIsConnected(true);
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data) as WebSocketMessage;
if (data.type === "new_message") {
setNewMessage(data as NewMessageEvent);
} else if (data.type === "read_receipt") {
setReadReceipt(data as ReadReceiptEvent);
} else if (data.type === "message_deleted") {
setMessageDeleted(data as MessageDeletedEvent);
}
} catch (error) {
console.error("Error parsing WebSocket message:", error);
}
};
ws.onclose = () => {
console.log("WebSocket connection closed");
setIsConnected(false);
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
};
setSocket(ws);
// Clean up on unmount
return () => {
if (
ws.readyState === WebSocket.OPEN ||
ws.readyState === WebSocket.CONNECTING
) {
ws.close();
}
};
}, [threadId, userId, token]);
// Send a message through WebSocket
const sendWebSocketMessage = (
content: string,
replyToId?: string | null,
// messageId?: string | null,
replyContent?: string | null
) => {
if (!socket || socket.readyState !== WebSocket.OPEN || !userId) {
console.error("WebSocket is not connected");
return false;
}
const message: SendMessagePayload = {
type: "new_message",
content,
// sender_id: userId, // Remove this as it's not needed in client->server communication
...(replyToId && { reply_to_id: replyToId }),
...(replyContent && { reply_content: replyContent }),
// ...(messageId && { message_id: messageId }),
};
socket.send(JSON.stringify(message));
return true;
};
// Mark messages as read
const markMessagesAsRead = () => {
if (!socket || socket.readyState !== WebSocket.OPEN || !userId) {
console.error("WebSocket is not connected");
return false;
}
const message: MarkReadPayload = {
type: "mark_read",
// reader_id: userId, // Remove this as it's not needed in client->server communication
};
socket.send(JSON.stringify(message));
return true;
};
// Delete a message
const deleteMessage = (messageId: string) => {
if (!socket || socket.readyState !== WebSocket.OPEN) {
console.error("WebSocket is not connected");
return false;
}
const message: DeleteMessagePayload = {
type: "delete_message",
message_id: messageId,
};
socket.send(JSON.stringify(message));
return true;
};
return {
isConnected,
sendWebSocketMessage,
markMessagesAsRead,
deleteMessage,
newMessage,
readReceipt,
messageDeleted,
// Reset message notification after handling
resetNewMessage: () => setNewMessage(null),
resetReadReceipt: () => setReadReceipt(null),
resetMessageDeleted: () => setMessageDeleted(null),
};
};
// WebSocket message types
interface WebSocketMessage {
type: string;
[key: string]: string | number | boolean | string[] | null | undefined;
}
interface SendMessagePayload extends WebSocketMessage {
type: "new_message";
content: string;
reply_to_id?: string | null;
reply_content?: string | null;
}
interface MarkReadPayload extends WebSocketMessage {
type: "mark_read";
}
interface NewMessageEvent extends WebSocketMessage {
type: "new_message";
message_id: string;
content: string;
sender_id: string;
created_at: string;
reply_to_id?: string | null;
reply_content?: string | null;
}
interface ReadReceiptEvent extends WebSocketMessage {
type: "read_receipt";
reader_username: string;
read_at: string;
}
interface DeleteMessagePayload extends WebSocketMessage {
type: "delete_message";
message_id: string;
}
interface MessageDeletedEvent extends WebSocketMessage {
type: "message_deleted";
message_id: string;
}
export default useWebSocket;