veza/apps/web/src/features/chat/components/ChatMessages.tsx

174 lines
6.4 KiB
TypeScript
Raw Normal View History

import { useEffect, useRef } from 'react';
import { useChatStore } from '@/stores/chat';
import { useAuthStore } from '@/stores/auth';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { sanitizeChatMessage } from '@/utils/sanitize';
import {
MoreVertical,
Reply,
Smile,
ThumbsUp,
ThumbsDown,
MessageSquare,
} from 'lucide-react';
export function ChatMessages() {
const { currentConversation, messages, typingUsers } = useChatStore();
const { user } = useAuthStore();
const messagesEndRef = useRef<HTMLDivElement>(null);
const conversationMessages = currentConversation
? messages[currentConversation.id] || []
: [];
const typingUserIds = currentConversation
? typingUsers[currentConversation.id] || []
: [];
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [conversationMessages]);
if (!currentConversation) {
return (
<div className='flex-1 flex items-center justify-center bg-muted/50'>
<div className='text-center'>
<MessageSquare className='h-12 w-12 mx-auto mb-4 text-muted-foreground' />
<h3 className='text-lg font-medium text-muted-foreground'>
Sélectionnez une conversation
</h3>
<p className='text-sm text-muted-foreground'>
Choisissez une conversation pour commencer à discuter
</p>
</div>
</div>
);
}
return (
<div className='flex-1 flex flex-col'>
{/* En-tête de la conversation */}
<div className='p-4 border-b bg-background'>
<div className='flex items-center justify-between'>
<div>
<h3 className='font-semibold'>
{currentConversation.name ||
`Conversation ${currentConversation.id.slice(0, 8)}`}
</h3>
<p className='text-sm text-muted-foreground'>
{currentConversation.participants.length} participant
{currentConversation.participants.length > 1 ? 's' : ''}
</p>
</div>
<Button variant='ghost' size='icon'>
<MoreVertical className='h-4 w-4' />
</Button>
</div>
</div>
{/* Messages */}
<div className='flex-1 overflow-y-auto p-4 space-y-4'>
{conversationMessages.length === 0 ? (
<div className='text-center text-muted-foreground'>
<p>Aucun message dans cette conversation</p>
</div>
) : (
conversationMessages.map(message => {
const isOwn = message.sender_id === user?.id;
return (
<div
key={message.id}
className={`flex ${isOwn ? 'justify-end' : 'justify-start'}`}
>
<div className={`max-w-[70%] ${isOwn ? 'order-2' : 'order-1'}`}>
<div className='flex items-center space-x-2 mb-1'>
<span className='text-xs text-muted-foreground'>
{isOwn ? 'Vous' : `Utilisateur ${message.sender_id}`}
</span>
<span className='text-xs text-muted-foreground'>
{new Date(message.created_at).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})}
</span>
</div>
<Card
className={`${isOwn ? 'bg-primary text-primary-foreground' : ''}`}
>
<CardContent className='p-3'>
<p
className='text-sm'
dangerouslySetInnerHTML={{
__html: sanitizeChatMessage(message.content)
}}
/>
{/* Réactions */}
{message.reactions && message.reactions.length > 0 && (
<div className='flex flex-wrap gap-1 mt-2'>
{message.reactions.map((reaction, index) => (
<Button
key={index}
variant='ghost'
size='sm'
className='h-6 px-2 text-xs'
>
{reaction.emoji} {reaction.user_id}
</Button>
))}
</div>
)}
</CardContent>
</Card>
{/* Actions sur le message */}
<div className='flex items-center space-x-1 mt-1 opacity-0 group-hover:opacity-100 transition-opacity'>
<Button variant='ghost' size='sm' className='h-6 px-2'>
<ThumbsUp className='h-3 w-3' />
</Button>
<Button variant='ghost' size='sm' className='h-6 px-2'>
<ThumbsDown className='h-3 w-3' />
</Button>
<Button variant='ghost' size='sm' className='h-6 px-2'>
<Reply className='h-3 w-3' />
</Button>
<Button variant='ghost' size='sm' className='h-6 px-2'>
<Smile className='h-3 w-3' />
</Button>
</div>
</div>
</div>
);
})
)}
{/* Indicateur de frappe */}
{typingUserIds.length > 0 && (
<div className='flex items-center space-x-2 text-sm text-muted-foreground'>
<div className='flex space-x-1'>
<div className='w-2 h-2 bg-muted-foreground rounded-full animate-bounce'></div>
<div
className='w-2 h-2 bg-muted-foreground rounded-full animate-bounce'
style={{ animationDelay: '0.1s' }}
></div>
<div
className='w-2 h-2 bg-muted-foreground rounded-full animate-bounce'
style={{ animationDelay: '0.2s' }}
></div>
</div>
<span>
{typingUserIds.length === 1
? "Quelqu'un"
: `${typingUserIds.length} personnes`}{' '}
est en train d'écrire...
</span>
</div>
)}
<div ref={messagesEndRef} />
</div>
</div>
);
}