Website (14 monoliths split): - compliance/page.tsx (1,519 → 9), docs/audit (1,262 → 20) - quality (1,231 → 16), alerts (1,203 → 10), docs (1,202 → 11) - i18n.ts (1,173 → 8 language files) - unity-bridge (1,094 → 12), backlog (1,087 → 6) - training (1,066 → 8), rag (1,063 → 8) - Deleted index_original.ts (4,899 LOC dead backup) Studio-v2 (5 monoliths split): - meet/page.tsx (1,481 → 9), messages (1,166 → 9) - AlertsB2BContext.tsx (1,165 → 5 modules) - alerts-b2b/page.tsx (1,019 → 6), korrektur/archiv (1,001 → 6) All existing imports preserved. Zero new TypeScript errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
167 lines
4.9 KiB
TypeScript
167 lines
4.9 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect, useMemo } from 'react'
|
|
import {
|
|
useMessages,
|
|
formatMessageDate,
|
|
type Conversation,
|
|
type Message,
|
|
type Contact,
|
|
} from '@/lib/MessagesContext'
|
|
|
|
export function useMessagesPage() {
|
|
const messagesCtx = useMessages()
|
|
const {
|
|
contacts,
|
|
conversations,
|
|
messages,
|
|
templates,
|
|
unreadCount,
|
|
recentConversations,
|
|
sendMessage,
|
|
markAsRead,
|
|
createConversation,
|
|
addReaction,
|
|
deleteMessage,
|
|
pinConversation,
|
|
muteConversation,
|
|
currentConversationId,
|
|
setCurrentConversationId,
|
|
} = messagesCtx
|
|
|
|
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)
|
|
|
|
// 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)
|
|
}
|
|
|
|
// Insert emoji
|
|
const handleEmojiSelect = (emoji: string) => {
|
|
setMessageInput(prev => prev + emoji)
|
|
}
|
|
|
|
// 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 })
|
|
}
|
|
|
|
// Close context menu on click outside
|
|
useEffect(() => {
|
|
const handleClick = () => setContextMenu(null)
|
|
if (contextMenu) {
|
|
document.addEventListener('click', handleClick)
|
|
return () => document.removeEventListener('click', handleClick)
|
|
}
|
|
}, [contextMenu])
|
|
|
|
const currentContact = currentConversation ? getConversationContact(currentConversation) : undefined
|
|
|
|
return {
|
|
// Context data
|
|
contacts, templates, unreadCount,
|
|
currentConversationId, currentConversation, currentContact,
|
|
filteredConversations, groupedMessages,
|
|
// UI state
|
|
messageInput, setMessageInput,
|
|
sendWithEmail, setSendWithEmail,
|
|
isSending,
|
|
showNewConversation, setShowNewConversation,
|
|
showEmojiPicker, setShowEmojiPicker,
|
|
showTemplates, setShowTemplates,
|
|
showContactInfo, setShowContactInfo,
|
|
searchQuery, setSearchQuery,
|
|
contextMenu, setContextMenu,
|
|
// Actions
|
|
selectConversation,
|
|
handleSendMessage,
|
|
handleEmojiSelect,
|
|
handleStartConversation,
|
|
handleContextMenu,
|
|
getSenderName,
|
|
getConversationContact,
|
|
// From context
|
|
pinConversation,
|
|
muteConversation,
|
|
deleteMessage,
|
|
}
|
|
}
|