'use client' import { useRef, useEffect, useState, useCallback } from 'react' import { useMissionControl, ChatMessage } from '@/store' import { MessageBubble } from './message-bubble' import { Button } from '@/components/ui/button' function formatDateGroup(timestamp: number): string { const date = new Date(timestamp * 1000) const today = new Date() const yesterday = new Date(today) yesterday.setDate(yesterday.getDate() - 1) if (date.toDateString() === today.toDateString()) return 'Today' if (date.toDateString() === yesterday.toDateString()) return 'Yesterday' return date.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }) } function groupMessagesByDate(messages: ChatMessage[]): Array<{ date: string; messages: ChatMessage[] }> { const groups: Array<{ date: string; messages: ChatMessage[] }> = [] let currentDate = '' for (const msg of messages) { const dateStr = formatDateGroup(msg.created_at) if (dateStr !== currentDate) { currentDate = dateStr groups.push({ date: dateStr, messages: [] }) } groups[groups.length - 1].messages.push(msg) } return groups } // Check if message should be visually grouped with previous function isGroupedWithPrevious(messages: ChatMessage[], index: number): boolean { if (index === 0) return false const prev = messages[index - 1] const curr = messages[index] // Same sender, within 2 minutes, not a system message return ( prev.from_agent === curr.from_agent && curr.created_at - prev.created_at < 120 && prev.message_type !== 'system' && curr.message_type !== 'system' ) } export function MessageList() { const { chatMessages, activeConversation, isSendingMessage, updatePendingMessage, removePendingMessage, addChatMessage } = useMissionControl() const bottomRef = useRef(null) const containerRef = useRef(null) const [showNewMessages, setShowNewMessages] = useState(false) const prevMessageCountRef = useRef(0) const isNearBottom = useCallback(() => { const container = containerRef.current if (!container) return true return container.scrollHeight - container.scrollTop - container.clientHeight < 120 }, []) // Auto-scroll to bottom on new messages (only if near bottom) useEffect(() => { const conversationMessages = chatMessages.filter(m => m.conversation_id === activeConversation) const newCount = conversationMessages.length if (newCount > prevMessageCountRef.current) { if (isNearBottom()) { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) } else { setShowNewMessages(true) } } prevMessageCountRef.current = newCount }, [chatMessages, activeConversation, isNearBottom]) // Scroll to bottom on conversation change useEffect(() => { bottomRef.current?.scrollIntoView() setShowNewMessages(false) prevMessageCountRef.current = 0 }, [activeConversation]) // Track scroll position to hide "new messages" indicator const handleScroll = useCallback(() => { if (isNearBottom()) { setShowNewMessages(false) } }, [isNearBottom]) const scrollToBottom = useCallback(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) setShowNewMessages(false) }, []) // Retry a failed message const handleRetry = async (msg: ChatMessage) => { updatePendingMessage(msg.id, { pendingStatus: 'sending' }) try { const res = await fetch('/api/chat/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ from: msg.from_agent, to: msg.to_agent, content: msg.content, conversation_id: msg.conversation_id, message_type: msg.message_type, forward: true, }), }) if (res.ok) { const data = await res.json() if (data.message) { // Remove temp message and add real one removePendingMessage(msg.id) addChatMessage(data.message) } } else { updatePendingMessage(msg.id, { pendingStatus: 'failed' }) } } catch { updatePendingMessage(msg.id, { pendingStatus: 'failed' }) } } if (!activeConversation) { return (

Select a conversation

or start a new one with an agent

) } const conversationMessages = chatMessages.filter( m => m.conversation_id === activeConversation ) if (conversationMessages.length === 0) { return (

No messages yet

Send a message to get started

) } const groups = groupMessagesByDate(conversationMessages) return (
{groups.map((group) => (
{/* Date separator */}
{group.date}
{group.messages.map((msg, idx) => (
{/* Failed message wrapper */} {msg.pendingStatus === 'failed' && (
Failed to send
)} {/* Normal or sending message */} {msg.pendingStatus !== 'failed' && ( )}
))}
))} {/* Typing indicator */} {isSendingMessage && (
)}
{/* New messages indicator */} {showNewMessages && ( )}
) }