import { formatChatDayLabel, toLocalDayKey } from '@/lib/formatters' interface DisplayableChatMessage { id: string senderId: string createdAt: string } export interface ChatDateSeparatorItem { kind: 'date' key: string label: string } export interface ChatMessageDisplayItem { kind: 'message' key: string message: TMessage showSenderName: boolean showAvatar: boolean isClusterStart: boolean isClusterEnd: boolean } export type ChatDisplayItem = | ChatDateSeparatorItem | ChatMessageDisplayItem const SAME_SENDER_GAP_MS = 5 * 60 * 1000 const getTime = (value: string) => { const time = new Date(value).getTime() return Number.isFinite(time) ? time : 0 } /** * Builds Telegram-like display rows: day separators + clustered messages. * Name on first of consecutive same-sender run; avatar on last of that run. */ export const groupMessagesForDisplay = ( messages: TMessage[], options?: { showSenderNames?: boolean; now?: Date } ): ChatDisplayItem[] => { const showSenderNames = options?.showSenderNames ?? false const now = options?.now ?? new Date() const items: ChatDisplayItem[] = [] let lastDayKey: string | null = null messages.forEach((message, index) => { const dayKey = toLocalDayKey(message.createdAt) ?? message.id if (dayKey !== lastDayKey) { items.push({ kind: 'date', key: `date-${dayKey}`, label: formatChatDayLabel(message.createdAt, now), }) lastDayKey = dayKey } const previousSameSender = index > 0 && messages[index - 1].senderId === message.senderId && toLocalDayKey(messages[index - 1].createdAt) === toLocalDayKey(message.createdAt) && getTime(message.createdAt) - getTime(messages[index - 1].createdAt) <= SAME_SENDER_GAP_MS const nextSameSender = index < messages.length - 1 && messages[index + 1].senderId === message.senderId && toLocalDayKey(messages[index + 1].createdAt) === toLocalDayKey(message.createdAt) && getTime(messages[index + 1].createdAt) - getTime(message.createdAt) <= SAME_SENDER_GAP_MS const isClusterStart = !previousSameSender const isClusterEnd = !nextSameSender items.push({ kind: 'message', key: message.id, message, showSenderName: showSenderNames && isClusterStart, showAvatar: showSenderNames && isClusterEnd, isClusterStart, isClusterEnd, }) }) return items }