import { useState, useEffect, useRef } from "react"; import { motion, AnimatePresence } from "motion/react"; import { MaterialDialog } from "@/core/components/Dialog"; import { RichTextArea } from "@/core/components/RichTextArea"; import type { Message } from "@/core/types"; import Quote from "@/core/components/Quote"; import { useImmer } from "use-immer"; import { EmojiMenu } from "./EmojiMenu"; import { MaterialButton, MaterialIcon, MaterialIconButton } from "@/utils/material"; 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; onTyping?: () => void; onStopTyping?: () => void; } export function ChatInputWrapper( { onSendMessage, onSaveEdit, replyTo, replyToVisible, onClearReply, onCloseReply, editingMessage, editVisible = false, onClearEdit, onCloseEdit, onProvideFileAdder, messagePanelRef, onTyping, onStopTyping }: 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 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(e: React.MouseEvent) { e.stopPropagation(); if (!emojiMenuOpen) { 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); } } else { setEmojiMenuOpen(false); } }; function handleEmojiSelect(emoji: string) { setMessage(prev => prev + emoji); }; function handleTyping() { if (onTyping) { onTyping(); } }; function handleMessageChange(value: string) { setMessage(value); handleTyping(); }; 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(); // Stop typing indicator when message is sent if (onStopTyping) onStopTyping(); } } }; 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 (
{editVisible && editingMessage && (
{editingMessage!.username} {editingMessage!.content}
)}
{replyToVisible && replyTo && (
{replyTo!.username} {replyTo!.content}
)}
setSelectedFiles([])}> {attachmentsVisible && selectedFiles.length > 0 && (
{selectedFiles.map((file, i) => ( { if (selectedFiles.length == 1) { setAttachmentsVisible(false); } else { setSelectedFiles(draft => { draft.splice(i) }) } }} > {file.name} ))}
setAttachmentsVisible(false)}>
)}
e.stopPropagation()} onMouseUp={e => e.stopPropagation()} className="emoji-btn" />
Ошибка
Общий размер вложений превышает 4 ГБ.
setErrorOpen(false)}>Закрыть
setEmojiMenuOpen(false)} onEmojiSelect={handleEmojiSelect} position={emojiMenuPosition} mode="standalone" />
); }