|
import React, { useState, useEffect, useRef } from 'react'; |
|
import { useAuth } from '../services/AuthContext'; |
|
import { useNavigate } from 'react-router-dom'; |
|
|
|
const processMessages = (messageObject) => { |
|
return (previousMessages) => { |
|
if (previousMessages.length > 0 && previousMessages[previousMessages.length - 1].sender === messageObject.sender) { |
|
const newMessage = {text:previousMessages[previousMessages.length - 1].text+ ' ' + messageObject.text, sender: messageObject.sender}; |
|
return [...previousMessages.slice(0, -1), newMessage]; |
|
} else { |
|
return [...previousMessages, messageObject]; |
|
} |
|
} |
|
} |
|
|
|
const Chat = () => { |
|
const { token, logout } = useAuth(); |
|
const navigate = useNavigate(); |
|
const [messages, setMessages] = useState([]); |
|
const [input, setInput] = useState(''); |
|
const [isConnected, setIsConnected] = useState(false); |
|
const [connectionAttempts, setConnectionAttempts] = useState(0); |
|
const [isReconnecting, setIsReconnecting] = useState(false); |
|
const websocket = useRef(null); |
|
const maxReconnectAttempts = 5; |
|
|
|
const connectWebSocket = () => { |
|
if (websocket.current?.readyState === WebSocket.OPEN) { |
|
console.log('WebSocket is already connected'); |
|
return; |
|
} |
|
|
|
console.log('Attempting to connect WebSocket...'); |
|
const ws = new WebSocket('ws://localhost:8000/ws'); |
|
websocket.current = ws; |
|
|
|
ws.onopen = async () => { |
|
console.log('WebSocket connected, sending auth token...'); |
|
|
|
ws.send(JSON.stringify({ |
|
type: 'authentication', |
|
token: token |
|
})); |
|
}; |
|
|
|
ws.onmessage = (event) => { |
|
try { |
|
const data = JSON.parse(event.data); |
|
console.log('Received message:', data); |
|
|
|
switch (data.type) { |
|
case 'connection_established': |
|
setIsConnected(true); |
|
setConnectionAttempts(0); |
|
setIsReconnecting(false); |
|
break; |
|
case 'message': |
|
setMessages(processMessages({ |
|
text:data.message, |
|
sender: data.sender |
|
})); |
|
break; |
|
case 'error': |
|
console.error('Server error:', data.message); |
|
if (data.message.includes('Authentication failed')) { |
|
logout(); |
|
navigate('/login'); |
|
} |
|
break; |
|
default: |
|
console.log('Unhandled message type:', data.type); |
|
} |
|
} catch (error) { |
|
console.error('Error processing message:', error); |
|
setMessages(processMessages({ |
|
text: event.data, |
|
sender: 'ai' |
|
})); |
|
} |
|
}; |
|
|
|
ws.onclose = (event) => { |
|
console.log('WebSocket closed:', event); |
|
setIsConnected(false); |
|
|
|
if (event.code === 1008) { |
|
|
|
console.error('WebSocket authentication failed'); |
|
logout(); |
|
navigate('/login'); |
|
} else if (connectionAttempts < maxReconnectAttempts) { |
|
|
|
|
|
setIsReconnecting(true); |
|
setConnectionAttempts(prev => prev + 1); |
|
setTimeout(connectWebSocket, 2000 * Math.min(connectionAttempts + 1, 5)); |
|
} |
|
}; |
|
|
|
ws.onerror = (error) => { |
|
console.error('WebSocket error:', error); |
|
}; |
|
}; |
|
|
|
useEffect(() => { |
|
connectWebSocket(); |
|
|
|
return () => { |
|
if (websocket.current) { |
|
websocket.current.close(); |
|
} |
|
}; |
|
}, [token]); |
|
|
|
const sendMessage = (e) => { |
|
e.preventDefault(); |
|
if (!input.trim() || !isConnected) return; |
|
|
|
const message = { |
|
type: 'message', |
|
content: input |
|
}; |
|
|
|
setMessages(processMessages({ |
|
text: input, |
|
sender: 'user' |
|
})); |
|
websocket.current.send(JSON.stringify(message)); |
|
setInput(''); |
|
}; |
|
console.log('MESSAGES****', messages); |
|
|
|
return ( |
|
<div className="flex flex-col h-screen bg-gray-100"> |
|
|
|
|
|
{/* Messages area */} |
|
<div className="flex-1 overflow-y-auto p-4 space-y-4"> |
|
{isReconnecting && <span className="text-yellow-500">Reconnecting...</span>} |
|
{(messages || []).map((message, index) => ( |
|
<div |
|
key={index} |
|
className={`p-3 rounded-lg max-w-[80%] ${message.sender === 'user' |
|
? 'ml-auto bg-blue-500 text-white' |
|
: message.sender === 'ai' |
|
? 'bg-gray-200' |
|
: 'bg-yellow-100 mx-auto' |
|
}`} |
|
> |
|
{message.text} |
|
</div> |
|
))} |
|
</div> |
|
|
|
{/* Input area */} |
|
<div className="p-4 bg-white border-t"> |
|
<form onSubmit={sendMessage} className="flex gap-4"> |
|
<input |
|
type="text" |
|
value={input} |
|
onChange={(e) => setInput(e.target.value)} |
|
placeholder="Type your message..." |
|
className="flex-1 p-2 border rounded" |
|
disabled={!isConnected} |
|
/> |
|
<button |
|
type="submit" |
|
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-gray-400" |
|
disabled={!isConnected} |
|
> |
|
Send |
|
</button> |
|
|
|
<button |
|
onClick={() => { |
|
logout(); |
|
navigate('/login'); |
|
}} |
|
className="px-4 py-2 text-white bg-red-500 rounded hover:bg-red-600" |
|
> |
|
Logout |
|
</button> |
|
<span className={`h-3 w-3 rounded-full ${isConnected ? 'bg-green-500' : 'bg-red-500'}`}></span> |
|
</form> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default Chat; |