/** * @file components/chat/ChatMessages.tsx * @description Scrollable container component that displays message history, handles date dividers, and renders individual chat items for both channels and direct messages. */ "use client"; import { useState, useEffect, useRef } from "react"; import { ChatItem, type MessageWithMember } from "./ChatItem"; /** Properties for the ChatMessages component. */ export interface ChatMessagesProps { type: "chat" | "dm"; name: string; initialMessages: MessageWithMember[]; onDeleteMessage?: (id: string) => void; onEditMessage?: (id: string, newContent: string) => void; } /** Formats a date string or Date object into a readable label (Today, Yesterday, or formatted date). */ function formatDateLabel(dateString: string | Date): string { const date = new Date(dateString); const now = new Date(); const isToday = date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(); const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1); const isYesterday = date.getDate() === yesterday.getDate() && date.getMonth() === yesterday.getMonth() && date.getFullYear() === yesterday.getFullYear(); if (isToday) return "Today"; if (isYesterday) return "Yesterday"; return date.toLocaleDateString("de-DE", { day: "numeric", month: "long", year: "numeric", }); } /** Renders the scrollable message feed with greetings, date dividers, and interactive chat items. */ export function ChatMessages({ type, name, initialMessages, onDeleteMessage, onEditMessage, }: ChatMessagesProps) { const [messages, setMessages] = useState(initialMessages); const scrollRef = useRef(null); useEffect(() => { setMessages(initialMessages); }, [initialMessages]); const scrollToBottom = () => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }; useEffect(() => { requestAnimationFrame(() => { scrollToBottom(); }); }, [messages]); /** * Handles local state update and triggers parent callback upon successful message deletion. * * @function handleDeleteMessage * @param {string} id - The unique identifier of the deleted message. * @returns {void} */ const handleDeleteMessage = (id: string) => { setMessages((prev) => prev.filter((m) => m.id !== id)); onDeleteMessage?.(id); }; /** * Handles local state update and triggers parent callback upon successful message edit. * * @function handleEditMessage * @param {string} id - The unique identifier of the edited message. * @param {string} newContent - The updated content text of the message. * @returns {void} */ const handleEditMessage = (id: string, newContent: string) => { setMessages((prev) => prev.map((m) => m.id === id ? { ...m, content: newContent, updatedAt: new Date() } : m, ), ); onEditMessage?.(id, newContent); }; return (
{/* Chat Header Greeting */}

{type === "dm" ? `Direct Messages with @${name}` : `Welcome to #${name}!`}

{type === "dm" ? `This is the start of your direct message history with @${name}.` : `This is the beginning of the channel #${name}.`}

{/* Message Feed */}
{messages.map((message, index) => { const currentDateLabel = formatDateLabel(message.createdAt); const previousMessage = messages[index - 1]; const previousDateLabel = previousMessage ? formatDateLabel(previousMessage.createdAt) : null; const showDateDivider = currentDateLabel !== previousDateLabel; return (
{showDateDivider && (
{currentDateLabel}
)}
); })}
); }