Spaces:
Configuration error
Configuration error
| import { useState } 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 { Badge } from '@/components/ui/badge' | |
| import { ScrollArea } from '@/components/ui/scroll-area' | |
| import { | |
| Search, | |
| Plus, | |
| Settings, | |
| MessageSquare, | |
| Users | |
| } from 'lucide-react' | |
| import { formatTime, getInitials } from '@/lib/utils' | |
| export default function ChatSidebar() { | |
| const { user } = useAuthStore() | |
| const { chats, currentChat, selectChat, loading } = useChatStore() | |
| const [searchQuery, setSearchQuery] = useState('') | |
| const filteredChats = chats.filter(chat => { | |
| if (!searchQuery) return true | |
| const searchLower = searchQuery.toLowerCase() | |
| // Search by chat name | |
| if (chat.name?.toLowerCase().includes(searchLower)) return true | |
| // Search by participant names (for direct chats) | |
| if (chat.type === 'direct') { | |
| const otherParticipant = chat.participants.find(p => p.userId !== user?.id) | |
| if (otherParticipant?.user.displayName?.toLowerCase().includes(searchLower)) return true | |
| } | |
| return false | |
| }) | |
| const getChatDisplayName = (chat: any) => { | |
| if (chat.type === 'group') { | |
| return chat.name || 'Unnamed Group' | |
| } else { | |
| const otherParticipant = chat.participants.find((p: any) => p.userId !== user?.id) | |
| return otherParticipant?.user.displayName || 'Unknown User' | |
| } | |
| } | |
| const getChatAvatar = (chat: any) => { | |
| if (chat.type === 'group') { | |
| return chat.avatar | |
| } else { | |
| const otherParticipant = chat.participants.find((p: any) => p.userId !== user?.id) | |
| return otherParticipant?.user.avatar | |
| } | |
| } | |
| return ( | |
| <div className="flex flex-col h-full bg-background"> | |
| {/* Header */} | |
| <div className="p-4 border-b border-border"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <h1 className="text-xl font-semibold">Chats</h1> | |
| <div className="flex items-center space-x-2"> | |
| <Button variant="ghost" size="icon"> | |
| <Plus className="w-4 h-4" /> | |
| </Button> | |
| <Button variant="ghost" size="icon"> | |
| <Settings className="w-4 h-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Search */} | |
| <div className="relative"> | |
| <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" /> | |
| <Input | |
| placeholder="Search chats..." | |
| value={searchQuery} | |
| onChange={(e) => setSearchQuery(e.target.value)} | |
| className="pl-10" | |
| /> | |
| </div> | |
| </div> | |
| {/* Chat List */} | |
| <ScrollArea className="flex-1"> | |
| <div className="p-2"> | |
| {loading ? ( | |
| <div className="flex items-center justify-center py-8"> | |
| <div className="text-muted-foreground">Loading chats...</div> | |
| </div> | |
| ) : filteredChats.length === 0 ? ( | |
| <div className="flex flex-col items-center justify-center py-8 text-center"> | |
| <MessageSquare className="w-12 h-12 text-muted-foreground mb-4" /> | |
| <h3 className="font-medium mb-2">No chats found</h3> | |
| <p className="text-sm text-muted-foreground mb-4"> | |
| {searchQuery ? 'Try a different search term' : 'Start a new conversation'} | |
| </p> | |
| <Button size="sm"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| New Chat | |
| </Button> | |
| </div> | |
| ) : ( | |
| filteredChats.map((chat) => ( | |
| <div | |
| key={chat.id} | |
| onClick={() => selectChat(chat.id)} | |
| className={` | |
| flex items-center p-3 rounded-lg cursor-pointer transition-colors | |
| hover:bg-accent hover:text-accent-foreground | |
| ${currentChat?.id === chat.id ? 'bg-accent text-accent-foreground' : ''} | |
| `} | |
| > | |
| <div className="relative"> | |
| <Avatar className="w-12 h-12"> | |
| <AvatarImage src={getChatAvatar(chat)} /> | |
| <AvatarFallback> | |
| {chat.type === 'group' ? ( | |
| <Users className="w-6 h-6" /> | |
| ) : ( | |
| getInitials(getChatDisplayName(chat)) | |
| )} | |
| </AvatarFallback> | |
| </Avatar> | |
| {/* Online indicator for direct chats */} | |
| {chat.type === 'direct' && ( | |
| <div className="absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-background rounded-full" /> | |
| )} | |
| </div> | |
| <div className="flex-1 ml-3 min-w-0"> | |
| <div className="flex items-center justify-between"> | |
| <h3 className="font-medium truncate"> | |
| {getChatDisplayName(chat)} | |
| </h3> | |
| <div className="flex items-center space-x-2"> | |
| {chat.lastMessage && ( | |
| <span className="text-xs text-muted-foreground"> | |
| {formatTime(chat.lastMessage.createdAt)} | |
| </span> | |
| )} | |
| {chat.unreadCount > 0 && ( | |
| <Badge variant="default" className="text-xs"> | |
| {chat.unreadCount > 99 ? '99+' : chat.unreadCount} | |
| </Badge> | |
| )} | |
| </div> | |
| </div> | |
| {chat.lastMessage && ( | |
| <p className="text-sm text-muted-foreground truncate mt-1"> | |
| {chat.lastMessage.type === 'text' | |
| ? chat.lastMessage.content | |
| : `📎 ${chat.lastMessage.type}` | |
| } | |
| </p> | |
| )} | |
| {chat.type === 'group' && ( | |
| <p className="text-xs text-muted-foreground mt-1"> | |
| {chat.participants.length} members | |
| </p> | |
| )} | |
| </div> | |
| </div> | |
| )) | |
| )} | |
| </div> | |
| </ScrollArea> | |
| </div> | |
| ) | |
| } | |