import { useState, useEffect, useRef } from "react"; import { MaterialDialog } from "../core/Dialog"; import { RichTextArea } from "../core/RichTextArea"; import type { Message } from "../../../core/types"; import Quote from "../core/Quote"; import AnimatedHeight from "../core/animations/AnimatedHeight"; import { useImmer } from "use-immer"; import { EmojiMenu } from "./EmojiMenu"; interface ChatInputWrapperProps { onSendMessage: (message: string, files: File[]) => void; onSaveEdit?: (content: string) => void; replyTo?: Message | null; replyToVisible: boolean; onClearReply?: () => void; onCloseReply?: () => void; editingMessage?: Message | null; editVisible?: boolean; onClearEdit?: () => void; onCloseEdit?: () => void; onProvideFileAdder?: (adder: (files: File[]) => void) => void; messagePanelRef?: React.RefObject; } export function ChatInputWrapper( { onSendMessage, onSaveEdit, replyTo, replyToVisible, onClearReply, onCloseReply, editingMessage, editVisible = false, onClearEdit, onCloseEdit, onProvideFileAdder, messagePanelRef }: ChatInputWrapperProps ) { const [message, setMessage] = useState(""); const [selectedFiles, setSelectedFiles] = useImmer([]); const [attachmentsVisible, setAttachmentsVisible] = useState(false); const [errorOpen, setErrorOpen] = useState(false); const [emojiMenuOpen, setEmojiMenuOpen] = useState(false); const [emojiMenuPosition, setEmojiMenuPosition] = useState({ x: 0, y: 0 }); const emojiButtonRef = useRef(null); const chatInputWrapperRef = useRef(null); // Expose a way for parent to programmatically add files useEffect(() => { if (onProvideFileAdder) { const addFiles = (files: File[]) => { if (!files || files.length === 0) return; setSelectedFiles(draft => { draft.push(...files) }); }; onProvideFileAdder(addFiles); } }, [onProvideFileAdder]); // When entering edit mode, preload the message content useEffect(() => { setMessage(editingMessage ? editingMessage.content || "" : ""); }, [editingMessage]); useEffect(() => { setAttachmentsVisible(selectedFiles.length > 0); }, [selectedFiles]); function handleEmojiButtonClick() { if (chatInputWrapperRef.current && messagePanelRef?.current) { const inputRect = chatInputWrapperRef.current.getBoundingClientRect(); const panelRect = messagePanelRef.current.getBoundingClientRect(); // Position menu 10px from message panel edge and 10px above the chat input // The animation will start 30px below this position setEmojiMenuPosition({ x: panelRect.left + 10, // 10px from message panel edge y: window.innerHeight - inputRect.top + 10 // 10px above the top of chat input }); setEmojiMenuOpen(true); } }; function handleEmojiSelect(emoji: string) { setMessage(prev => prev + emoji); }; async function handleSubmit(e: React.FormEvent | Event) { e.preventDefault(); const hasText = Boolean(message.trim()); const hasFiles = selectedFiles.length > 0; if (hasText || hasFiles) { const totalSize = selectedFiles.reduce((acc, f) => acc + f.size, 0); const limit = 4 * 1024 * 1024 * 1024; // 4GB if (totalSize > limit) { setErrorOpen(true); return; } if (editingMessage && onSaveEdit) { onSaveEdit(message); setMessage(""); if (onClearEdit) onClearEdit(); } else { onSendMessage(message, selectedFiles); setMessage(""); setAttachmentsVisible(false); if (onClearReply) onClearReply(); } } }; function handleAttachClick() { const input = document.createElement("input"); input.type = "file"; input.multiple = true; input.addEventListener("change", () => { setSelectedFiles(draft => { draft.push(...Array.from(input.files || [])) }); }); input.click(); } return (
{editingMessage && (
{editingMessage!.username} {editingMessage!.content}
)}
{replyTo && (
{replyTo!.username} {replyTo!.content}
)}
setSelectedFiles([])}> {selectedFiles.length > 0 && (
{selectedFiles.map((file, i) => ( { if (selectedFiles.length == 1) { setAttachmentsVisible(false); } else { setSelectedFiles(draft => { draft.splice(i) }) } }} > {file.name} ))}
setAttachmentsVisible(false)}>
)}
setMessage(value)} onEnter={handleSubmit} />
Ошибка
Общий размер вложений превышает 4 ГБ.
setErrorOpen(false)}>Закрыть
setEmojiMenuOpen(false)} onEmojiSelect={handleEmojiSelect} position={emojiMenuPosition} />
); }