Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions app/protected/messages/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export default function MessagesPage() {
// Track user's online presence
useMyPresence()

// Prevent body scroll
useEffect(() => {
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = 'unset'
}
}, [])

// Get conversation from URL params
useEffect(() => {
const conversationId = searchParams.get('conversation')
Expand Down Expand Up @@ -53,15 +61,20 @@ export default function MessagesPage() {
: 'Unknown'

return (
<div className="flex flex-col h-[calc(100vh-4rem)]">
<div className="flex flex-col h-[100dvh] bg-black overflow-hidden absolute inset-0">
{/* Header */}
<div className="border-b bg-background p-4">
<div className="border-b border-zinc-800 bg-black p-4 flex-shrink-0">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-3">
<MessageSquare className="h-6 w-6 text-primary" />
<h1 className="text-xl md:text-2xl font-bold">Messages</h1>
<div className="p-2 rounded-lg bg-gradient-to-br from-blue-500 to-purple-600">
<MessageSquare className="h-5 w-5 text-white" />
</div>
<h1 className="text-xl md:text-2xl font-bold text-white">Messages</h1>
</div>
<Button onClick={() => setShowNewMessage(true)} className="gap-2">
<Button
onClick={() => setShowNewMessage(true)}
className="gap-2 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white border-0"
>
<Plus className="h-4 w-4" />
<span className="hidden sm:inline">New Message</span>
<span className="sm:hidden">New</span>
Expand All @@ -74,18 +87,18 @@ export default function MessagesPage() {
<div className="max-w-7xl mx-auto h-full flex">
{/* Sidebar - Conversation List (Hidden on mobile when conversation is selected) */}
<div className={`
w-full md:w-80 md:border-r flex flex-col bg-background
w-full md:w-80 md:border-r md:border-zinc-800 flex flex-col bg-black
${selectedConversationId ? 'hidden md:flex' : 'flex'}
`}>
{/* Search */}
<div className="p-3 border-b">
<div className="p-3 border-b border-zinc-800">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-zinc-500" />
<Input
placeholder="Search conversations..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9"
className="pl-9 bg-zinc-900 border-zinc-800 text-white placeholder:text-zinc-500 focus:border-blue-500 focus:ring-blue-500"
/>
</div>
</div>
Expand All @@ -103,17 +116,17 @@ export default function MessagesPage() {

{/* Main Area - Conversation View (Hidden on mobile when no conversation selected) */}
<div className={`
flex-1 bg-background flex flex-col
flex-1 bg-black flex flex-col
${selectedConversationId ? 'flex' : 'hidden md:flex'}
`}>
{selectedConversationId && selectedConversation && (
<div className="border-b p-3 md:p-4 bg-muted/50 flex-shrink-0">
<div className="border-b border-zinc-800 p-3 md:p-4 bg-zinc-900/50 flex-shrink-0">
<div className="flex items-center gap-2 md:gap-3">
{/* Back button for mobile */}
<Button
variant="ghost"
size="icon"
className="md:hidden"
className="md:hidden text-white hover:bg-zinc-800"
onClick={() => setSelectedConversationId(null)}
>
<svg
Expand All @@ -130,7 +143,7 @@ export default function MessagesPage() {
<path d="m15 18-6-6 6-6"/>
</svg>
</Button>
<h2 className="font-semibold text-sm md:text-base truncate">{conversationName}</h2>
<h2 className="font-semibold text-sm md:text-base truncate text-white">{conversationName}</h2>
{!selectedConversation.is_group && selectedConversation.other_user && selectedConversation.other_user.id ? (
<UserStatusIndicator
userId={selectedConversation.other_user.id}
Expand Down
27 changes: 16 additions & 11 deletions components/messages/ConversationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ export function ConversationList({ conversations, selectedId, onSelect, loading
if (conversations.length === 0) {
return (
<div className="flex flex-col items-center justify-center h-full p-8 text-center">
<MessageCircle className="h-12 w-12 text-muted-foreground mb-4" />
<h3 className="text-lg font-semibold mb-2">No conversations yet</h3>
<p className="text-sm text-muted-foreground">
<div className="p-4 rounded-full bg-zinc-900 mb-4">
<MessageCircle className="h-12 w-12 text-zinc-600" />
</div>
<h3 className="text-lg font-semibold mb-2 text-white">No conversations yet</h3>
<p className="text-sm text-zinc-400">
Start a new conversation to get started
</p>
</div>
Expand Down Expand Up @@ -87,13 +89,13 @@ export function ConversationList({ conversations, selectedId, onSelect, loading
key={conversation.id}
onClick={() => onSelect(conversation.id)}
className={cn(
'w-full flex items-center gap-3 p-3 rounded-lg transition-colors text-left',
'hover:bg-muted',
isSelected && 'bg-muted'
'w-full flex items-center gap-3 p-3 rounded-lg transition-all text-left',
'hover:bg-zinc-900',
isSelected && 'bg-zinc-900 border border-zinc-800'
)}
>
<div className="relative">
<Avatar className="w-12 h-12 flex-shrink-0">
<Avatar className="w-12 h-12 flex-shrink-0 ring-2 ring-zinc-800">
{avatarUrl && <AvatarImage src={avatarUrl} alt={name} />}
<AvatarFallback className="bg-gradient-to-br from-blue-500 to-purple-600 text-white">
{initials}
Expand All @@ -111,24 +113,27 @@ export function ConversationList({ conversations, selectedId, onSelect, loading

<div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-1">
<span className={cn('font-semibold truncate text-sm md:text-base', conversation.unread_count > 0 && 'text-primary')}>
<span className={cn(
'font-semibold truncate text-sm md:text-base',
conversation.unread_count > 0 ? 'text-blue-400' : 'text-white'
)}>
{name}
</span>
{conversation.last_message_at && (
<span className="text-[10px] md:text-xs text-muted-foreground flex-shrink-0 ml-2">
<span className="text-[10px] md:text-xs text-zinc-500 flex-shrink-0 ml-2">
{formatDistanceToNow(new Date(conversation.last_message_at), { addSuffix: true })}
</span>
)}
</div>
<div className="flex items-center justify-between">
<p className={cn(
'text-xs md:text-sm truncate',
conversation.unread_count > 0 ? 'font-medium text-foreground' : 'text-muted-foreground'
conversation.unread_count > 0 ? 'font-medium text-zinc-300' : 'text-zinc-500'
)}>
{conversation.last_message_content || 'No messages yet'}
</p>
{conversation.unread_count > 0 && (
<Badge variant="default" className="ml-2 flex-shrink-0 text-xs">
<Badge className="ml-2 flex-shrink-0 text-xs bg-gradient-to-r from-blue-500 to-purple-600 text-white border-0">
{conversation.unread_count}
</Badge>
)}
Expand Down
22 changes: 13 additions & 9 deletions components/messages/ConversationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ export function ConversationView({ conversationId }: ConversationViewProps) {

if (!conversationId) {
return (
<div className="h-full flex flex-col items-center justify-center p-8 text-center">
<MessageSquare className="h-16 w-16 text-muted-foreground mb-4" />
<h3 className="text-xl font-semibold mb-2">Select a conversation</h3>
<p className="text-muted-foreground">
<div className="h-full flex flex-col items-center justify-center p-8 text-center bg-black">
<div className="p-6 rounded-full bg-zinc-900 mb-6">
<MessageSquare className="h-16 w-16 text-zinc-600" />
</div>
<h3 className="text-xl font-semibold mb-2 text-white">Select a conversation</h3>
<p className="text-zinc-400">
Choose a conversation from the list to start messaging
</p>
</div>
Expand All @@ -57,14 +59,16 @@ export function ConversationView({ conversationId }: ConversationViewProps) {
}

return (
<div className="h-full flex flex-col">
<div className="h-full flex flex-col bg-black">
{/* Messages Area */}
<div className="flex-1 overflow-y-auto p-4 min-h-0">
{messages.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full text-center">
<MessageSquare className="h-12 w-12 text-muted-foreground mb-4" />
<h3 className="text-lg font-semibold mb-2">No messages yet</h3>
<p className="text-sm text-muted-foreground">
<div className="p-4 rounded-full bg-zinc-900 mb-4">
<MessageSquare className="h-12 w-12 text-zinc-600" />
</div>
<h3 className="text-lg font-semibold mb-2 text-white">No messages yet</h3>
<p className="text-sm text-zinc-400">
Send a message to start the conversation
</p>
</div>
Expand All @@ -88,7 +92,7 @@ export function ConversationView({ conversationId }: ConversationViewProps) {
)}

{/* Message Input */}
<div className="flex-shrink-0">
<div className="flex-shrink-0 border-t border-zinc-800">
<MessageInput
onSend={sendMessage}
disabled={sending}
Expand Down
8 changes: 4 additions & 4 deletions components/messages/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ export function MessageBubble({ message, isOwn }: MessageBubbleProps) {

<div className={cn('flex flex-col', isOwn ? 'items-end' : 'items-start', 'max-w-[70%]')}>
{!isOwn && (
<span className="text-xs font-medium text-muted-foreground mb-1">
<span className="text-xs font-medium text-zinc-400 mb-1">
{getName()}
</span>
)}

<div
className={cn(
'rounded-2xl px-4 py-2 break-words',
'rounded-2xl px-4 py-2 break-words shadow-lg',
isOwn
? 'bg-gradient-to-br from-blue-500 to-purple-600 text-white'
: 'bg-muted text-foreground',
: 'bg-zinc-900 text-white border border-zinc-800',
message.is_deleted && 'italic opacity-60'
)}
>
Expand All @@ -59,7 +59,7 @@ export function MessageBubble({ message, isOwn }: MessageBubbleProps) {
)}
</div>

<span className="text-xs text-muted-foreground mt-1">
<span className="text-xs text-zinc-500 mt-1">
{formatDistanceToNow(new Date(message.created_at), { addSuffix: true })}
</span>
</div>
Expand Down
8 changes: 4 additions & 4 deletions components/messages/MessageInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function MessageInput({ onSend, disabled, placeholder = 'Type a message..
}, [content, isTyping, onTyping])

return (
<form onSubmit={handleSubmit} className="border-t bg-background p-2 md:p-3">
<form onSubmit={handleSubmit} className="bg-black p-3 md:p-4">
<div className="flex gap-2 items-end max-w-full">
<Textarea
ref={textareaRef}
Expand All @@ -99,14 +99,14 @@ export function MessageInput({ onSend, disabled, placeholder = 'Type a message..
onKeyDown={handleKeyDown}
placeholder={placeholder}
disabled={disabled || sending}
className="min-h-[40px] md:min-h-[44px] max-h-[120px] resize-none flex-1 text-sm md:text-base"
className="min-h-[40px] md:min-h-[44px] max-h-[120px] resize-none flex-1 text-sm md:text-base bg-zinc-900 border-zinc-800 text-white placeholder:text-zinc-500 focus:border-blue-500 focus:ring-blue-500"
rows={1}
/>
<Button
type="submit"
size="icon"
disabled={!content.trim() || sending || disabled}
className="flex-shrink-0 h-10 w-10 md:h-11 md:w-11"
className="flex-shrink-0 h-10 w-10 md:h-11 md:w-11 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white border-0"
>
{sending ? (
<Loader2 className="h-4 w-4 animate-spin" />
Expand All @@ -115,7 +115,7 @@ export function MessageInput({ onSend, disabled, placeholder = 'Type a message..
)}
</Button>
</div>
<p className="text-[10px] md:text-xs text-muted-foreground mt-1 md:mt-1.5 hidden sm:block">
<p className="text-[10px] md:text-xs text-zinc-500 mt-1.5 hidden sm:block">
Press Enter to send, Shift+Enter for new line
</p>
</form>
Expand Down
8 changes: 4 additions & 4 deletions components/messages/TypingIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export function TypingIndicator({ usernames }: TypingIndicatorProps) {
if (usernames.length === 0) return null

return (
<div className="flex items-center gap-2 px-4 py-2 text-sm text-muted-foreground">
<div className="flex items-center gap-2 px-4 py-2 text-sm text-zinc-400 bg-black">
<div className="flex gap-1">
<span className="w-2 h-2 bg-muted-foreground rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
<span className="w-2 h-2 bg-muted-foreground rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
<span className="w-2 h-2 bg-muted-foreground rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
<span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
<span className="w-2 h-2 bg-purple-500 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
<span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
</div>
<span>Typing...</span>
</div>
Expand Down
Loading