import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from 'react-query'; import { conversationAPI, chatAPI } from '../services/api'; import { useSocket } from './useSocket'; import toast from 'react-hot-toast'; export const useChat = () => { const [currentConversationId, setCurrentConversationId] = useState(null); const [messages, setMessages] = useState([]); const [isTyping, setIsTyping] = useState(false); const queryClient = useQueryClient(); const socket = useSocket(); // Get conversations const { data: conversations = [], isLoading: conversationsLoading, refetch: refetchConversations } = useQuery('conversations', () => conversationAPI.getConversations().then(res => res.data.conversations)); // Get messages for current conversation const { data: messagesData, isLoading: messagesLoading, refetch: refetchMessages } = useQuery( ['messages', currentConversationId], () => chatAPI.getMessages(currentConversationId).then(res => res.data.messages), { enabled: !!currentConversationId, onSuccess: (data) => { setMessages(data); } } ); // Create conversation mutation const createConversationMutation = useMutation( (title) => conversationAPI.createConversation(title), { onSuccess: (response) => { const newConversation = response.data.conversation; setCurrentConversationId(newConversation._id); queryClient.invalidateQueries('conversations'); toast.success('New conversation created'); }, onError: (error) => { toast.error(error.response?.data?.error || 'Failed to create conversation'); } } ); // Send message mutation const sendMessageMutation = useMutation( ({ conversationId, content }) => chatAPI.sendMessage(conversationId, content), { onSuccess: (response) => { const { userMessage, assistantMessage } = response.data; setMessages(prev => [...prev, userMessage, assistantMessage]); queryClient.invalidateQueries('conversations'); }, onError: (error) => { toast.error(error.response?.data?.error || 'Failed to send message'); } } ); // Delete message mutation const deleteMessageMutation = useMutation( (messageId) => chatAPI.deleteMessage(messageId), { onSuccess: () => { refetchMessages(); queryClient.invalidateQueries('conversations'); toast.success('Message deleted'); }, onError: (error) => { toast.error(error.response?.data?.error || 'Failed to delete message'); } } ); // Update conversation mutation const updateConversationMutation = useMutation( ({ id, updates }) => conversationAPI.updateConversation(id, updates), { onSuccess: () => { queryClient.invalidateQueries('conversations'); toast.success('Conversation updated'); }, onError: (error) => { toast.error(error.response?.data?.error || 'Failed to update conversation'); } } ); // Delete conversation mutation const deleteConversationMutation = useMutation( (id) => conversationAPI.deleteConversation(id), { onSuccess: () => { if (currentConversationId === id) { setCurrentConversationId(null); setMessages([]); } queryClient.invalidateQueries('conversations'); toast.success('Conversation deleted'); }, onError: (error) => { toast.error(error.response?.data?.error || 'Failed to delete conversation'); } } ); // Socket event handlers useEffect(() => { if (!socket) return; const handleNewMessage = (data) => { const { userMessage, assistantMessage } = data; setMessages(prev => [...prev, userMessage, assistantMessage]); queryClient.invalidateQueries('conversations'); }; const handleUserTyping = (data) => { if (data.conversationId === currentConversationId) { setIsTyping(data.isTyping); } }; socket.on('newMessage', handleNewMessage); socket.on('userTyping', handleUserTyping); return () => { socket.off('newMessage', handleNewMessage); socket.off('userTyping', handleUserTyping); }; }, [socket, currentConversationId, queryClient]); // Helper functions const createConversation = (title = 'New Chat') => { createConversationMutation.mutate(title); }; const sendMessage = (content) => { if (!currentConversationId) { // Create new conversation first const title = content.length > 50 ? content.substring(0, 50) + '...' : content; createConversationMutation.mutate(title); // The message will be sent after conversation is created return; } sendMessageMutation.mutate({ conversationId: currentConversationId, content }); }; const deleteMessage = (messageId) => { deleteMessageMutation.mutate(messageId); }; const updateConversation = (id, updates) => { updateConversationMutation.mutate({ id, updates }); }; const deleteConversation = (id) => { deleteConversationMutation.mutate(id); }; const switchConversation = (conversationId) => { setCurrentConversationId(conversationId); setMessages([]); }; const emitTyping = (isTyping) => { if (socket && currentConversationId) { socket.emit('typing', { conversationId: currentConversationId, isTyping }); } }; return { // Data conversations, messages, currentConversationId, isTyping, // Loading states conversationsLoading, messagesLoading, sendingMessage: sendMessageMutation.isLoading, creatingConversation: createConversationMutation.isLoading, // Actions createConversation, sendMessage, deleteMessage, updateConversation, deleteConversation, switchConversation, emitTyping, // Utilities refetchConversations, refetchMessages }; };