'use client' import React, { useState, useEffect, useRef } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { PaperPlaneRight, MagnifyingGlass, UserCircle, WarningCircle, List, X } from '@phosphor-icons/react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import rehypeHighlight from 'rehype-highlight' import Window from './Window' import { useKV } from '../hooks/useKV' interface Message { id: string text: string sender: string userId: string timestamp: number } interface MessagesProps { onClose: () => void onMinimize?: () => void onMaximize?: () => void onFocus?: () => void zIndex?: number } export function Messages({ onClose, onMinimize, onMaximize, onFocus, zIndex }: MessagesProps) { const [messages, setMessages] = useState([]) const [inputText, setInputText] = useState('') const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const messagesEndRef = useRef(null) const [sidebarOpen, setSidebarOpen] = useState(false) // Persistent user identity const [userId] = useKV('messages-user-id', `user-${Math.random().toString(36).substring(2, 9)}`) const [userName, setUserName] = useKV('messages-user-name', 'Guest') const [isEditingName, setIsEditingName] = useState(false) const [tempName, setTempName] = useState(userName) const fetchMessages = async () => { try { const res = await fetch('/api/messages') if (res.ok) { const data = await res.json() setMessages(data) } } catch (err) { console.error('Failed to fetch messages', err) } } // Poll for new messages useEffect(() => { fetchMessages() const interval = setInterval(fetchMessages, 3000) return () => clearInterval(interval) }, []) // Scroll to bottom on new messages useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages]) const handleSend = async (e?: React.FormEvent) => { e?.preventDefault() if (!inputText.trim() || isLoading) return setIsLoading(true) setError(null) try { const res = await fetch('/api/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: inputText, sender: userName, userId: userId }) }) const data = await res.json() if (!res.ok) { setError(data.error || 'Failed to send message') } else { setMessages(data) setInputText('') } } catch (err) { setError('Network error. Please try again.') } finally { setIsLoading(false) } } const handleNameSave = () => { if (tempName.trim()) { setUserName(tempName.trim()) setIsEditingName(false) } } return (
{/* Mobile Sidebar Overlay */} {sidebarOpen && ( setSidebarOpen(false)} /> )} {/* Sidebar */} {(sidebarOpen || typeof window !== 'undefined' && window.innerWidth >= 768) && (
Global Chat Now
{messages.length > 0 ? messages[messages.length - 1].text.replace(/[#*`_~\[\]]/g, '') : 'No messages yet'}
{/* User Profile */}
{isEditingName ? (
setTempName(e.target.value)} className="flex-1 bg-white/10 rounded px-2 py-1 text-sm outline-none border border-blue-500 min-w-0" autoFocus onKeyDown={(e) => e.key === 'Enter' && handleNameSave()} />
) : (
{ setTempName(userName) setIsEditingName(true) }}>
{userName}
Click to change name
)}
)}
{/* Chat Area */}
{/* Header */}
To: Everyone Global Channel
Public Chat
{/* Messages List */}
{messages.map((msg, index) => { const isMe = msg.userId === userId const showHeader = index === 0 || messages[index - 1].userId !== msg.userId const showTimestamp = index === 0 || (msg.timestamp - messages[index - 1].timestamp > 300000) // 5 mins return ( {showTimestamp && (
{new Date(msg.timestamp).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}
)} {showHeader && !isMe && ( {msg.sender} )}
{msg.text}
{/* Tail for message bubbles */} {isMe && showHeader && ( )} {!isMe && showHeader && ( )}
{/* Delivered status for me */} {isMe && index === messages.length - 1 && ( Delivered )}
) })}
{/* Input Area */}
{error && ( {error} )}
setInputText(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSend(e) } }} onPaste={(e) => { // Allow paste - default behavior e.stopPropagation() }} placeholder="iMessage" maxLength={200} disabled={isLoading} className="w-full bg-[#2a2a2a] border border-white/10 rounded-full py-1.5 sm:py-2 pl-3 sm:pl-4 pr-9 sm:pr-10 text-xs sm:text-sm text-white placeholder-gray-500 focus:outline-none focus:border-gray-500 transition-colors select-text" />
{inputText.length}/200 • Enter to send
) }