cnmksjs's picture
Upload 60 files
590318c verified
import { useState, useRef, useEffect } from 'react'
import { useChatStore } from '@/store/chatStore'
import { useAuthStore } from '@/store/authStore'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { ScrollArea } from '@/components/ui/scroll-area'
import {
Send,
Paperclip,
Smile,
Phone,
Video,
MoreVertical,
Users,
Info
} from 'lucide-react'
import { formatTime, getInitials } from '@/lib/utils'
export default function ChatWindow() {
const { user } = useAuthStore()
const { currentChat, messages, sendMessage, loading } = useChatStore()
const [messageText, setMessageText] = useState('')
const messagesEndRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const chatMessages = currentChat ? messages[currentChat.id] || [] : []
useEffect(() => {
scrollToBottom()
}, [chatMessages])
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}
const handleSendMessage = async () => {
if (!messageText.trim() || !currentChat) return
try {
await sendMessage(currentChat.id, messageText.trim())
setMessageText('')
inputRef.current?.focus()
} catch (error) {
console.error('Failed to send message:', error)
}
}
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSendMessage()
}
}
const getChatDisplayName = () => {
if (!currentChat) return ''
if (currentChat.type === 'group') {
return currentChat.name || 'Unnamed Group'
} else {
const otherParticipant = currentChat.participants.find(p => p.userId !== user?.id)
return otherParticipant?.user.displayName || 'Unknown User'
}
}
const getChatAvatar = () => {
if (!currentChat) return undefined
if (currentChat.type === 'group') {
return currentChat.avatar
} else {
const otherParticipant = currentChat.participants.find(p => p.userId !== user?.id)
return otherParticipant?.user.avatar
}
}
const isMessageFromCurrentUser = (message: any) => {
return message.senderId === user?.id
}
if (!currentChat) {
return (
<div className="flex-1 flex items-center justify-center">
<div className="text-center">
<div className="text-6xl mb-4">πŸ’¬</div>
<h2 className="text-2xl font-semibold mb-2">Welcome to ChatApp</h2>
<p className="text-muted-foreground">
Select a chat to start messaging
</p>
</div>
</div>
)
}
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-border">
<div className="flex items-center space-x-3">
<Avatar className="w-10 h-10">
<AvatarImage src={getChatAvatar()} />
<AvatarFallback>
{currentChat.type === 'group' ? (
<Users className="w-5 h-5" />
) : (
getInitials(getChatDisplayName())
)}
</AvatarFallback>
</Avatar>
<div>
<h2 className="font-semibold">{getChatDisplayName()}</h2>
<p className="text-sm text-muted-foreground">
{currentChat.type === 'group'
? `${currentChat.participants.length} members`
: 'Online'
}
</p>
</div>
</div>
<div className="flex items-center space-x-2">
<Button variant="ghost" size="icon">
<Phone className="w-4 h-4" />
</Button>
<Button variant="ghost" size="icon">
<Video className="w-4 h-4" />
</Button>
<Button variant="ghost" size="icon">
<Info className="w-4 h-4" />
</Button>
<Button variant="ghost" size="icon">
<MoreVertical className="w-4 h-4" />
</Button>
</div>
</div>
{/* Messages */}
<ScrollArea className="flex-1 p-4">
<div className="space-y-4">
{loading ? (
<div className="flex items-center justify-center py-8">
<div className="text-muted-foreground">Loading messages...</div>
</div>
) : chatMessages.length === 0 ? (
<div className="flex items-center justify-center py-8">
<div className="text-center">
<div className="text-4xl mb-4">πŸ‘‹</div>
<p className="text-muted-foreground">
No messages yet. Start the conversation!
</p>
</div>
</div>
) : (
chatMessages.map((message, index) => {
const isFromCurrentUser = isMessageFromCurrentUser(message)
const showAvatar = !isFromCurrentUser && (
index === 0 ||
chatMessages[index - 1]?.senderId !== message.senderId
)
return (
<div
key={message.id}
className={`flex ${isFromCurrentUser ? 'justify-end' : 'justify-start'}`}
>
<div className={`flex max-w-[70%] ${isFromCurrentUser ? 'flex-row-reverse' : 'flex-row'}`}>
{showAvatar && !isFromCurrentUser && (
<Avatar className="w-8 h-8 mr-2">
<AvatarImage src={message.sender?.avatar} />
<AvatarFallback>
{getInitials(message.sender?.displayName || 'U')}
</AvatarFallback>
</Avatar>
)}
<div className={`${!showAvatar && !isFromCurrentUser ? 'ml-10' : ''}`}>
<div
className={`
px-4 py-2 rounded-lg
${isFromCurrentUser
? 'bg-primary text-primary-foreground'
: 'bg-muted'
}
`}
>
{!isFromCurrentUser && showAvatar && currentChat.type === 'group' && (
<p className="text-xs font-medium mb-1 opacity-70">
{message.sender?.displayName}
</p>
)}
<p className="text-sm">{message.content}</p>
<div className={`flex items-center justify-end mt-1 space-x-1`}>
<span className="text-xs opacity-70">
{formatTime(message.createdAt)}
</span>
{isFromCurrentUser && (
<span className="text-xs opacity-70">
{message.status === 'sent' && 'βœ“'}
{message.status === 'delivered' && 'βœ“βœ“'}
{message.status === 'read' && 'βœ“βœ“'}
</span>
)}
</div>
</div>
</div>
</div>
</div>
)
})
)}
<div ref={messagesEndRef} />
</div>
</ScrollArea>
{/* Message Input */}
<div className="p-4 border-t border-border">
<div className="flex items-center space-x-2">
<Button variant="ghost" size="icon">
<Paperclip className="w-4 h-4" />
</Button>
<div className="flex-1 relative">
<Input
ref={inputRef}
placeholder="Type a message..."
value={messageText}
onChange={(e) => setMessageText(e.target.value)}
onKeyPress={handleKeyPress}
className="pr-10"
/>
<Button
variant="ghost"
size="icon"
className="absolute right-1 top-1/2 transform -translate-y-1/2"
>
<Smile className="w-4 h-4" />
</Button>
</div>
<Button
onClick={handleSendMessage}
disabled={!messageText.trim()}
size="icon"
>
<Send className="w-4 h-4" />
</Button>
</div>
</div>
</div>
)
}