// SPDX-License-Identifier: AGPL-3.0-or-later import {BlockedMessageGroups} from '@app/features/channel/components/BlockedMessageGroups'; import {Divider} from '@app/features/channel/components/ChannelDivider'; import {getUnreadDividerBeforeMessageId} from '@app/features/channel/components/ChannelMessageStreamUtils'; import styles from '@app/features/channel/components/ChannelMessages.module.css'; import {MessageGroup} from '@app/features/channel/components/MessageGroup'; import type {Channel} from '@app/features/channel/models/Channel'; import type {Message} from '@app/features/messaging/models/MessagingMessage'; import type {ChannelMessages} from '@app/features/messaging/state/ChannelMessages'; import {type ChannelStreamItem, ChannelStreamType} from '@app/features/messaging/utils/MessageGroupingUtils'; import {IS_DEV} from '@app/features/platform/types/Env'; import {Logger} from '@app/features/platform/utils/AppLogger'; import type {MessagePreviewContext} from '@fluxer/constants/src/ChannelConstants'; import {MessageTypes} from '@fluxer/constants/src/ChannelConstants'; import type React from 'react'; const logger = new Logger('ChannelMessageStream'); const isSystemMessage = (message: Message | undefined): boolean => { if (!message) return false; return message.type !== MessageTypes.DEFAULT && message.type !== MessageTypes.REPLY; }; type MessageGroupKind = 'system' | 'regular'; const getMessageGroupKind = (message: Message | undefined): MessageGroupKind => { return isSystemMessage(message) ? 'system' : 'regular'; }; interface RenderChannelStreamProps { channelStream: Array; messages: ChannelMessages; channel: Channel; highlightedMessageId: string | null; messageDisplayCompact: boolean; messageGroupSpacing: number; unblurredMessageId: string | null; onMessageEdit?: (target: HTMLElement) => void; onReveal?: (messageId: string | null) => void; messageRowClassName?: string; messageActionsClassName?: string; renderMessageActions?: (message: Message) => React.ReactNode; suppressMessageActions?: boolean; previewContext?: keyof typeof MessagePreviewContext; dateDividerClassName?: string; suppressUnreadIndicator?: boolean; getMessageHeadingActivate?: (message: Message) => (() => void) | undefined; } export function renderChannelStream(props: RenderChannelStreamProps): Array { const { channelStream, channel, highlightedMessageId, messageDisplayCompact, messageGroupSpacing, unblurredMessageId, onMessageEdit, onReveal, messageRowClassName, messageActionsClassName, renderMessageActions, suppressMessageActions, previewContext, dateDividerClassName, suppressUnreadIndicator, getMessageHeadingActivate, } = props; const nodes: Array = []; const seenKeys = IS_DEV ? new Map}>() : null; const registerKey = (key: string | undefined, type: string, index: number, detail: Record = {}) => { if (!IS_DEV || !key || !seenKeys) return; const existing = seenKeys.get(key); if (existing) { logger.warn('Duplicate channel stream key detected', { key, existing, next: {type, index, detail}, }); return; } seenKeys.set(key, {type, index, detail}); }; let pendingMessages: Array = []; let pendingStreamItems: Array = []; let pendingGroupId: string | undefined; let pendingFlashKey: number | undefined; let lastRenderedGroupKind: MessageGroupKind | null = null; let spacerCounter = 0; let currentIndex = -1; const pushSpacerIfNeeded = (nextKind: MessageGroupKind, keyBase: string, nextMessageHasUnreadDivider = false) => { if (messageGroupSpacing <= 0 || lastRenderedGroupKind == null) return; if (nextMessageHasUnreadDivider) return; const bothSystem = lastRenderedGroupKind === 'system' && nextKind === 'system'; const spacerClass = bothSystem ? styles.groupSpacerHalf : styles.groupSpacer; nodes.push(