deepsite / ChatPanel.jsx
AchyuthKolli's picture
Upload 49 files
b074e91 verified
raw
history blame
5.12 kB
import React, { useState, useEffect, useRef } from 'react';
import { apiClient } from 'app';
import { MessageResponse } from 'types';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { ScrollArea } from '@/components/ui/scroll-area';
import { MessageCircle, Send, X } from 'lucide-react';
interface Props {
tableId: string;
userId: string;
isOpen: boolean;
onToggle: () => void;
}
export const ChatPanel: React.FC<Props> = ({ tableId, userId, isOpen, onToggle }) => {
const [messages, setMessages] = useState<MessageResponse[]>([]);
const [newMessage, setNewMessage] = useState('');
const [sending, setSending] = useState(false);
const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => {
loadMessages();
const interval = setInterval(loadMessages, 2000); // Poll every 2s
return () => clearInterval(interval);
}, [tableId]);
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [messages]);
const loadMessages = async () => {
try {
const response = await apiClient.get_messages({ table_id: tableId });
const data = await response.json();
setMessages(data.messages || []);
} catch (error) {
console.error('Failed to load messages:', error);
}
};
const sendMessage = async () => {
if (!newMessage.trim() || sending) return;
setSending(true);
try {
await apiClient.send_message({ table_id: tableId, message: newMessage.trim() });
setNewMessage('');
await loadMessages();
} catch (error) {
console.error('Failed to send message:', error);
} finally {
setSending(false);
}
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
};
return (
<>
{/* Toggle Button */}
{!isOpen && (
<button
onClick={onToggle}
className="fixed right-4 bottom-4 bg-green-600 hover:bg-green-700 text-white p-4 rounded-full shadow-lg z-50 transition-all"
title="Open Chat"
>
<MessageCircle className="w-6 h-6" />
</button>
)}
{/* Chat Sidebar */}
{isOpen && (
<div className="fixed right-0 top-0 h-full w-80 bg-slate-900 border-l border-slate-700 shadow-2xl z-50 flex flex-col">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-slate-700 bg-slate-800">
<h3 className="font-bold text-white flex items-center gap-2">
<MessageCircle className="w-5 h-5 text-green-500" />
Table Chat
</h3>
<Button variant="ghost" size="sm" onClick={onToggle}>
<X className="w-4 h-4" />
</Button>
</div>
{/* Messages */}
<ScrollArea className="flex-1 p-4" ref={scrollRef}>
<div className="space-y-3">
{messages.map((msg) => (
<div
key={msg.id}
className={`p-3 rounded-lg ${
msg.user_id === userId
? 'bg-green-600/20 border border-green-600/30 ml-8'
: msg.is_system
? 'bg-amber-600/20 border border-amber-600/30 text-center'
: 'bg-slate-800 border border-slate-700 mr-8'
}`}
>
{!msg.is_system && (
<div className="text-xs text-slate-400 mb-1">
{msg.user_id === userId ? 'You' : msg.user_email?.split('@')[0] || 'Player'}
{msg.private_to && <span className="ml-2 text-amber-400">(Private)</span>}
</div>
)}
<div className={`text-sm ${
msg.is_system ? 'text-amber-300 font-medium' : 'text-white'
}`}>
{msg.message}
</div>
<div className="text-xs text-slate-500 mt-1">
{new Date(msg.created_at).toLocaleTimeString()}
</div>
</div>
))}
</div>
</ScrollArea>
{/* Input */}
<div className="p-4 border-t border-slate-700 bg-slate-800">
<div className="flex gap-2">
<Input
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type a message..."
disabled={sending}
className="flex-1 bg-slate-900 border-slate-700 text-white"
/>
<Button
onClick={sendMessage}
disabled={!newMessage.trim() || sending}
className="bg-green-600 hover:bg-green-700"
>
<Send className="w-4 h-4" />
</Button>
</div>
</div>
</div>
)}
</>
);
};