richlai's picture
add lib
a6626cb
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;