Spaces:
Sleeping
Sleeping
import React, { useState, useEffect, useCallback } from 'react'; | |
import { Input } from "@/components/ui/input"; | |
import { Button } from "@/components/ui/button"; | |
import { establishPeerConnection } from '../webrtc'; | |
import { clearMessages } from '../api' | |
interface ChatProps { | |
token: string; | |
onLogout: () => void; | |
} | |
type ObjectMessage = { | |
id?: number; | |
content: string; | |
}; | |
type Message = string | ObjectMessage; | |
const Chat: React.FC<ChatProps> = ({ token, onLogout }) => { | |
const [message, setMessage] = useState(''); | |
const [messages, setMessages] = useState<Message[]>([]); | |
const [peerConnection, setPeerConnection] = useState<RTCPeerConnection | null>(null); | |
const [dataChannel, setDataChannel] = useState<RTCDataChannel | null>(null); | |
const setupPeerConnection = useCallback(async () => { | |
try { | |
const { pc, dc } = await establishPeerConnection(token); | |
setPeerConnection(pc); | |
setDataChannel(dc); | |
peerConnection; | |
dc.onmessage = (event) => { | |
setMessages((prevMessages) => { | |
let lastMessage = prevMessages[prevMessages.length - 1]; | |
if (!lastMessage || typeof lastMessage == 'string') { | |
let aiMessage: Message = { content: event.data } | |
return [...prevMessages, aiMessage]; | |
} else { | |
const updatedMessage = {...lastMessage} | |
updatedMessage.content += event.data; | |
return [...prevMessages.slice(0, -1), updatedMessage]; | |
} | |
}); | |
}; | |
} catch (error) { | |
console.error('Failed to establish peer connection:', error); | |
onLogout(); // Logout if connection fails | |
} | |
}, [token, onLogout]); | |
useEffect(() => { | |
setupPeerConnection(); | |
}, [setupPeerConnection]); | |
const sendMessage = () => { | |
if (dataChannel && dataChannel.readyState === 'open') { | |
dataChannel.send(message); | |
setMessages((prevMessages) => [...prevMessages, `You: ${message}`]); | |
setMessage(''); | |
} else { | |
console.error('Data channel is not open'); | |
} | |
}; | |
const renderMessage = (message: any, index: number) => { | |
const dateOptions: Intl.DateTimeFormatOptions = { | |
hour: "2-digit", | |
minute: "2-digit", | |
}; | |
const date = new Date().toLocaleTimeString( | |
undefined, | |
dateOptions | |
); | |
if (typeof message === 'string') { | |
return ( | |
<div key={index} className="flex items-start space-x-2"> | |
<div className="flex-1 border rounded-lg p-2"> | |
<div dangerouslySetInnerHTML={{ __html: message.replace(/\n/g, '<br/>') }} /> | |
</div> | |
<small className="text-xs text-gray-500">{date}</small> | |
</div> | |
); | |
} else { | |
return ( | |
<div key={index} className="flex items-start space-x-2"> | |
<div className="flex-1 border rounded-lg p-2"> | |
<div dangerouslySetInnerHTML={{ __html: message.content.replace(/\n/g, '<br/>') }} /> | |
</div> | |
<small className="text-xs text-gray-500">{date}</small> | |
</div> | |
); | |
} | |
}; | |
return ( | |
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 flex flex-col"> | |
<div className="flex-1 overflow-auto p-6"> | |
<div className="space-y-4"> | |
{messages.map((message, index) => renderMessage(message, index))} | |
</div> | |
</div> | |
<div className="border-t p-4 bg-white dark:bg-gray-800"> | |
<div className="flex items-center space-x-2"> | |
<Input | |
autoFocus | |
className="flex-1" | |
id="message-input" | |
placeholder="Type a message" | |
value={message} | |
onChange={(e) => setMessage(e.target.value)} | |
onKeyUp={(e) => { | |
if (e.key === "Enter") { | |
sendMessage(); | |
} | |
}} | |
/> | |
<Button onClick={sendMessage} type="submit" disabled={!message}> | |
Send | |
</Button> | |
<Button onClick={() => clearMessages(token)} type="submit"> | |
Reset | |
</Button> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default Chat; |