'use client'
import { useState, useEffect, useRef, useMemo } from 'react'
import { useRouter } from 'next/navigation'
import { Sidebar } from '@/components/Sidebar'
import { useLanguage } from '@/lib/LanguageContext'
import { useTheme } from '@/lib/ThemeContext'
import {
useMessages,
formatMessageTime,
formatMessageDate,
getContactInitials,
getRoleLabel,
getRoleColor,
emojiCategories,
type Conversation,
type Message,
type Contact
} from '@/lib/MessagesContext'
// ============================================
// EMOJI PICKER COMPONENT
// ============================================
function EmojiPicker({
onSelect,
onClose,
isDark
}: {
onSelect: (emoji: string) => void
onClose: () => void
isDark: boolean
}) {
const [activeCategory, setActiveCategory] = useState('Hรคufig')
return (
{/* Header */}
{/* Category Tabs */}
{Object.keys(emojiCategories).map(cat => (
))}
{/* Emoji Grid */}
{emojiCategories[activeCategory as keyof typeof emojiCategories].map((emoji, i) => (
))}
)
}
// ============================================
// MESSAGE TEMPLATES DROPDOWN
// ============================================
function TemplatesDropdown({
templates,
onSelect,
isDark
}: {
templates: { id: string; name: string; content: string }[]
onSelect: (content: string) => void
isDark: boolean
}) {
return (
Vorlagen
{templates.map(tpl => (
))}
)
}
// ============================================
// CONTACT INFO PANEL
// ============================================
function ContactInfoPanel({
contact,
conversation,
onClose,
isDark
}: {
contact: Contact | undefined
conversation: Conversation
onClose: () => void
isDark: boolean
}) {
return (
{/* Header */}
{/* Content */}
{/* Avatar & Name */}
{conversation.title ? getContactInitials(conversation.title) : '?'}
{conversation.title}
{contact && (
{getRoleLabel(contact.role)}
)}
{/* Contact Details */}
{contact && (
{contact.email && (
E-Mail
{contact.email}
)}
{contact.phone && (
Telefon
{contact.phone}
)}
{contact.student_name && (
Schueler/in
{contact.student_name} ({contact.class_name})
)}
{contact.tags.length > 0 && (
Tags
{contact.tags.map(tag => (
{tag}
))}
)}
)}
{/* Group Members */}
{conversation.is_group && (
{conversation.participant_ids.length} Mitglieder
)}
)
}
// ============================================
// MAIN PAGE
// ============================================
export default function MessagesPage() {
const { t } = useLanguage()
const { isDark } = useTheme()
const router = useRouter()
const {
contacts,
conversations,
messages,
templates,
unreadCount,
recentConversations,
sendMessage,
markAsRead,
createConversation,
addReaction,
deleteMessage,
pinConversation,
muteConversation,
currentConversationId,
setCurrentConversationId
} = useMessages()
const [messageInput, setMessageInput] = useState('')
const [sendWithEmail, setSendWithEmail] = useState(false)
const [isSending, setIsSending] = useState(false)
const [showNewConversation, setShowNewConversation] = useState(false)
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
const [showTemplates, setShowTemplates] = useState(false)
const [showContactInfo, setShowContactInfo] = useState(false)
const [searchQuery, setSearchQuery] = useState('')
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; messageId: string } | null>(null)
const messagesEndRef = useRef(null)
const inputRef = useRef(null)
// Current conversation data
const currentConversation = conversations.find(c => c.id === currentConversationId)
const currentMessages = currentConversationId ? (messages[currentConversationId] || []) : []
// Find contact for conversation
const getConversationContact = (conv: Conversation): Contact | undefined => {
if (conv.is_group) return undefined
return contacts.find(c => conv.participant_ids.includes(c.id))
}
// Get sender name for group messages
const getSenderName = (senderId: string): string => {
if (senderId === 'self') return 'Du'
const contact = contacts.find(c => c.id === senderId)
return contact?.name?.split(' ')[0] || 'Unbekannt'
}
// Filter conversations by search
const filteredConversations = useMemo(() => {
if (!searchQuery) return recentConversations
const q = searchQuery.toLowerCase()
return recentConversations.filter(c =>
c.title?.toLowerCase().includes(q) ||
c.last_message?.toLowerCase().includes(q)
)
}, [recentConversations, searchQuery])
// Group messages by date
const groupedMessages = useMemo(() => {
const groups: { date: string; messages: Message[] }[] = []
let currentDate = ''
for (const msg of currentMessages) {
const msgDate = formatMessageDate(msg.timestamp)
if (msgDate !== currentDate) {
currentDate = msgDate
groups.push({ date: msgDate, messages: [] })
}
groups[groups.length - 1].messages.push(msg)
}
return groups
}, [currentMessages])
// Select conversation
const selectConversation = async (conv: Conversation) => {
setCurrentConversationId(conv.id)
if (conv.unread_count > 0) {
await markAsRead(conv.id)
}
setShowContactInfo(false)
}
// Send message
const handleSendMessage = async () => {
if (!messageInput.trim() || !currentConversationId) return
setIsSending(true)
await sendMessage(currentConversationId, messageInput.trim(), sendWithEmail)
setMessageInput('')
setIsSending(false)
setShowEmojiPicker(false)
setShowTemplates(false)
inputRef.current?.focus()
}
// Insert emoji
const handleEmojiSelect = (emoji: string) => {
setMessageInput(prev => prev + emoji)
inputRef.current?.focus()
}
// Start new conversation
const handleStartConversation = async (contact: Contact) => {
const conv = await createConversation(contact.id)
if (conv) {
setCurrentConversationId(conv.id)
setShowNewConversation(false)
}
}
// Handle context menu
const handleContextMenu = (e: React.MouseEvent, messageId: string) => {
e.preventDefault()
setContextMenu({ x: e.clientX, y: e.clientY, messageId })
}
// Scroll to bottom
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [currentMessages])
// Close context menu on click outside
useEffect(() => {
const handleClick = () => setContextMenu(null)
if (contextMenu) {
document.addEventListener('click', handleClick)
return () => document.removeEventListener('click', handleClick)
}
}, [contextMenu])
// Handle Enter key
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSendMessage()
}
}
const currentContact = currentConversation ? getConversationContact(currentConversation) : undefined
return (
{/* Animated Background Blobs */}
{/* Sidebar */}
{/* Main Content */}
{/* Conversations List */}
{/* Header */}
Nachrichten
{unreadCount > 0 && (
{unreadCount} ungelesen
)}
{/* Search */}
{/* Conversation List */}
{filteredConversations.length === 0 ? (
๐ฌ
Keine Konversationen
Starten Sie eine neue Unterhaltung!
) : (
{filteredConversations.map((conv) => {
const contact = getConversationContact(conv)
const isActive = currentConversationId === conv.id
return (
)
})}
)}
{/* Chat Area */}
{currentConversation ? (
<>
{/* Chat Header */}
{/* Avatar */}
{currentConversation.title ? getContactInitials(currentConversation.title) : '?'}
{!currentConversation.is_group && currentContact?.online && (
)}
{currentConversation.title || 'Unbenannt'}
{currentConversation.typing ? (
schreibt...
) : currentContact ? (
<>
{getRoleLabel(currentContact.role)}
{currentContact.student_name && (
โข {currentContact.student_name}
)}
{currentContact.online && (
โข Online
)}
>
) : currentConversation.is_group && (
{currentConversation.participant_ids.length} Mitglieder
)}
{/* Actions */}
{/* Messages */}
{groupedMessages.length === 0 ? (
๐
Noch keine Nachrichten
Starten Sie die Konversation!
) : (
groupedMessages.map((group, groupIndex) => (
{/* Date Separator */}
{group.date}
{/* Messages */}
{group.messages.map((msg) => {
const isSelf = msg.sender_id === 'self'
const isGroupMsg = currentConversation.is_group && !isSelf
return (
handleContextMenu(e, msg.id)}
>
{/* Sender name for groups */}
{isGroupMsg && (
{getSenderName(msg.sender_id)}
)}
{msg.content}
{/* Reactions */}
{msg.reactions && msg.reactions.length > 0 && (
{msg.reactions.map((r, i) => (
{r.emoji}
))}
)}
{/* Time & Status */}
{new Date(msg.timestamp).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
{isSelf && (
<>
{msg.delivered && (
)}
{msg.email_sent && (
โ๏ธ
)}
>
)}
)
})}
))
)}
{/* Message Input */}
{/* Options Row */}
{/* Input Row */}
{/* Emoji Button */}
{showEmojiPicker && (
setShowEmojiPicker(false)}
isDark={isDark}
/>
)}
{/* Templates Button */}
{showTemplates && (
{
setMessageInput(content)
setShowTemplates(false)
inputRef.current?.focus()
}}
isDark={isDark}
/>
)}
{/* Text Input */}
>
) : (
/* No Conversation Selected */
๐ฌ
BreakPilot Messenger
Kommunizieren Sie sicher mit Eltern und Kollegen.
Waehlen Sie eine Konversation aus der Liste oder starten Sie eine neue Unterhaltung.
)}
{/* Contact Info Panel */}
{showContactInfo && currentConversation && (
setShowContactInfo(false)}
isDark={isDark}
/>
)}
{/* New Conversation Modal */}
{showNewConversation && (
{/* Header */}
Neue Nachricht
{/* Contact List */}
{contacts.length === 0 ? (
) : (
{contacts.map((contact) => (
))}
)}
)}
{/* Context Menu */}
{contextMenu && (
)}
{/* Blob Animation Styles */}
)
}