import * as React from "react"; import { ArrowDown, X } from "lucide-react"; import type { MainTimelineEntry } from "@/features/messages/lib/threadPanel"; import type { TimelineMessage } from "@/features/messages/types"; import type { UserProfileLookup } from "@/features/profile/lib/identity"; import type { Channel } from "@/shared/api/types"; import { Button } from "@/shared/ui/button"; import { MessageComposer } from "./MessageComposer"; import { MessageRow } from "./MessageRow"; import { MessageThreadSummaryRow } from "./MessageThreadSummaryRow"; import { TypingIndicatorRow } from "./TypingIndicatorRow"; import { useTimelineScrollManager } from "./useTimelineScrollManager"; type MessageThreadPanelProps = { canResetWidth: boolean; channel: Channel | null; channelId: string | null; channelName: string; currentPubkey?: string; disabled?: boolean; editTarget?: { author: string; body: string; id: string } | null; isSending: boolean; onCancelEdit?: () => void; onCancelReply: () => void; onClose: () => void; onDelete?: (message: TimelineMessage) => void; onEdit?: (message: TimelineMessage) => void; onEditSave?: (content: string) => Promise; onExpandReplies: (message: TimelineMessage) => void; onResetWidth: () => void; onResizeStart: (event: React.PointerEvent) => void; onScrollTargetResolved: () => void; onSelectReplyTarget: (message: TimelineMessage) => void; onSend: ( content: string, mentionPubkeys: string[], mediaTags?: string[][], ) => Promise; onToggleReaction?: ( message: TimelineMessage, emoji: string, remove: boolean, ) => Promise; profiles?: UserProfileLookup; replyTargetId: string | null; replyTargetMessage: TimelineMessage | null; scrollTargetId: string | null; threadHead: TimelineMessage | null; threadReplies: MainTimelineEntry[]; threadTypingPubkeys: string[]; widthPx: number; }; function canManageMessage( message: TimelineMessage, currentPubkey: string | undefined, ): boolean { return Boolean( currentPubkey && message.pubkey && currentPubkey.toLowerCase() === message.pubkey.toLowerCase(), ); } export function MessageThreadPanel({ canResetWidth, channel, channelId, channelName, currentPubkey, disabled = false, editTarget, isSending, onCancelEdit, onCancelReply, onClose, onDelete, onEdit, onEditSave, onExpandReplies, onResetWidth, onResizeStart, onScrollTargetResolved, onSelectReplyTarget, onSend, onToggleReaction, profiles, replyTargetId, replyTargetMessage, scrollTargetId, threadHead, threadReplies, threadTypingPubkeys, widthPx, }: MessageThreadPanelProps) { const threadBodyRef = React.useRef(null); const threadHeadId = threadHead?.id ?? null; const composerReplyTarget = replyTargetMessage && threadHead && replyTargetMessage.id !== threadHead.id ? { author: replyTargetMessage.author, body: replyTargetMessage.body, id: replyTargetMessage.id, } : null; const threadMessages = React.useMemo( () => threadReplies.map((entry) => entry.message), [threadReplies], ); const { bottomAnchorRef, contentRef, isAtBottom, newMessageCount, scrollToBottom, syncScrollState, } = useTimelineScrollManager({ channelId: threadHeadId, isLoading: false, messages: threadMessages, onTargetReached: onScrollTargetResolved, scrollContainerRef: threadBodyRef, targetMessageId: scrollTargetId, }); if (!threadHead) { return null; } return ( ); }