import { useState, useEffect, useRef } from 'react'; import { useAuth } from '@/hooks/useAuth'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { PageHeader } from '@/components/ui/page-header'; import { useToast } from '@/hooks/use-toast'; import Sidebar from '@/components/layout/Sidebar'; import { MessageCircle, Search, Send, UserPlus, Settings, Plus, Users, Clock } from 'lucide-react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; export default function ChatPage() { const { user } = useAuth(); const { toast } = useToast(); const queryClient = useQueryClient(); const [selectedChat, setSelectedChat] = useState(null); const [newMessage, setNewMessage] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [contactOpen, setContactOpen] = useState(false); const [chatOpen, setChatOpen] = useState(false); const messagesEndRef = useRef(null); const [newContact, setNewContact] = useState({ name: '', email: '', phone: '', notes: '' }); const [newChat, setNewChat] = useState({ name: '', type: 'group' as 'direct' | 'group', description: '' }); // Fetch chats const { data: chats, isLoading } = useQuery({ queryKey: ['/api/chats'], queryFn: async () => { const response = await fetch('/api/chats'); if (!response.ok) throw new Error('Failed to fetch chats'); return response.json(); }, enabled: !!user }); // Fetch contacts const { data: contacts } = useQuery({ queryKey: ['/api/contacts'], queryFn: async () => { const response = await fetch('/api/contacts'); if (!response.ok) throw new Error('Failed to fetch contacts'); return response.json(); }, enabled: !!user }); // Fetch messages for selected chat const { data: messages } = useQuery({ queryKey: ['/api/chats', selectedChat, 'messages'], queryFn: async () => { const response = await fetch(`/api/chats/${selectedChat}/messages`); if (!response.ok) throw new Error('Failed to fetch messages'); return response.json(); }, enabled: !!selectedChat }); // Create contact mutation const createContactMutation = useMutation({ mutationFn: async (contactData: typeof newContact) => { const response = await fetch('/api/contacts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(contactData) }); if (!response.ok) throw new Error('Failed to create contact'); return response.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/contacts'] }); setContactOpen(false); setNewContact({ name: '', email: '', phone: '', notes: '' }); toast({ title: "Contact added successfully!" }); }, onError: () => { toast({ title: "Failed to add contact", variant: "destructive" }); } }); // Create chat mutation const createChatMutation = useMutation({ mutationFn: async (chatData: typeof newChat) => { const response = await fetch('/api/chats', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(chatData) }); if (!response.ok) throw new Error('Failed to create chat'); return response.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/chats'] }); setChatOpen(false); setNewChat({ name: '', type: 'group', description: '' }); toast({ title: "Chat created successfully!" }); }, onError: () => { toast({ title: "Failed to create chat", variant: "destructive" }); } }); // Send message mutation const sendMessageMutation = useMutation({ mutationFn: async (messageData: { content: string; type: string }) => { const response = await fetch(`/api/chats/${selectedChat}/messages`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(messageData) }); if (!response.ok) throw new Error('Failed to send message'); return response.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/chats', selectedChat, 'messages'] }); setNewMessage(''); }, onError: () => { toast({ title: "Failed to send message", variant: "destructive" }); } }); const handleCreateContact = (e: React.FormEvent) => { e.preventDefault(); if (!newContact.name) { toast({ title: "Please enter a contact name", variant: "destructive" }); return; } createContactMutation.mutate(newContact); }; const handleCreateChat = (e: React.FormEvent) => { e.preventDefault(); if (!newChat.name) { toast({ title: "Please enter a chat name", variant: "destructive" }); return; } createChatMutation.mutate(newChat); }; const handleSendMessage = (e: React.FormEvent) => { e.preventDefault(); if (!newMessage.trim() || !selectedChat) return; sendMessageMutation.mutate({ content: newMessage, type: 'text' }); }; // WebSocket connection for real-time messaging useEffect(() => { if (!user) return; const wsUrl = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const ws = new WebSocket(`${wsUrl}//${window.location.host}/ws`); ws.onopen = () => { console.log('Connected to chat WebSocket'); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'new_message') { queryClient.invalidateQueries({ queryKey: ['/api/chats', data.chatId, 'messages'] }); queryClient.invalidateQueries({ queryKey: ['/api/chats'] }); } }; ws.onclose = () => { console.log('Chat WebSocket disconnected'); }; return () => { ws.close(); }; }, [user, queryClient]); // Auto-scroll to bottom of messages useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const filteredChats = chats?.filter((chat: any) => chat.name?.toLowerCase().includes(searchQuery.toLowerCase()) ) || []; if (isLoading) { return (
); } return (
Add New Contact Add a new contact to your network.
setNewContact({ ...newContact, name: e.target.value })} required />
setNewContact({ ...newContact, email: e.target.value })} />
setNewContact({ ...newContact, phone: e.target.value })} />