Spaces:
Configuration error
Configuration error
File size: 6,555 Bytes
2c6bb7b 590318c 2c6bb7b 590318c 2c6bb7b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
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>
)
}
|