import React, { useState, useEffect, useRef, useCallback } from 'react'; import type { ChatSession, ChatMessage, UploadedFile } from '../types'; import { ChatMessageItem } from './ChatMessageItem'; import { SuggestionButton } from './SuggestionButton'; import { SendIcon, FilmIcon, PaperclipIcon, XCircleIcon as XCircleIconFile, DocumentTextIcon, StopIcon } from './icons'; import { SUGGESTION_PROMPTS, REELBOT_IMAGE_URL } from '../constants'; const MAX_FILE_SIZE_MB = 5; const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024; const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; const ALLOWED_DOC_TYPES = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const ALLOWED_FILE_TYPES = [...ALLOWED_IMAGE_TYPES, ...ALLOWED_DOC_TYPES]; interface ChatViewProps { activeChatSession: ChatSession | null; onSendMessage: (message: string, file?: UploadedFile, isSuggestion?: boolean) => Promise; isLoading: boolean; error: string | null; onStopGeneration: () => void; } export const ChatView: React.FC = ({ activeChatSession, onSendMessage, isLoading, error, onStopGeneration }) => { const [userInput, setUserInput] = useState(''); const [selectedFile, setSelectedFile] = useState(null); const [fileError, setFileError] = useState(null); const messagesEndRef = useRef(null); const inputRef = useRef(null); const fileInputRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(scrollToBottom, [activeChatSession?.messages]); useEffect(() => { if (!isLoading && inputRef.current) { inputRef.current.focus(); } }, [isLoading, activeChatSession]); const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; setFileError(null); setSelectedFile(null); if (file) { if (file.size > MAX_FILE_SIZE_BYTES) { setFileError(`El archivo es demasiado grande (máx. ${MAX_FILE_SIZE_MB}MB).`); return; } if (!ALLOWED_FILE_TYPES.includes(file.type)) { setFileError('Tipo de archivo no admitido.'); return; } const fileInfo: UploadedFile = { name: file.name, type: file.type, size: file.size, }; if (ALLOWED_IMAGE_TYPES.includes(file.type)) { const reader = new FileReader(); reader.onloadend = () => { fileInfo.dataUrl = reader.result as string; setSelectedFile(fileInfo); }; reader.onerror = () => { setFileError('Error al leer el archivo de imagen.'); } reader.readAsDataURL(file); } else if (ALLOWED_DOC_TYPES.includes(file.type)) { setSelectedFile(fileInfo); } } if (fileInputRef.current) { fileInputRef.current.value = ""; } }; const clearSelectedFile = () => { setSelectedFile(null); setFileError(null); if (fileInputRef.current) { fileInputRef.current.value = ""; } }; const handleSubmit = (e?: React.FormEvent) => { e?.preventDefault(); if ((userInput.trim() || selectedFile) && !isLoading) { onSendMessage(userInput.trim(), selectedFile || undefined); setUserInput(''); clearSelectedFile(); } }; const handleSuggestionClick = (promptText: string) => { if (!isLoading) { onSendMessage(promptText, undefined, true); setUserInput(''); clearSelectedFile(); } }; const lastMessage = activeChatSession?.messages?.[activeChatSession.messages.length - 1]; const modelIsCurrentlyStreaming = isLoading && lastMessage?.sender === 'model' && lastMessage?.isStreaming; const modelIsThinking = isLoading && !modelIsCurrentlyStreaming; let placeholderText = "Describe tu audiencia y el objetivo de tu Reel..."; if (modelIsThinking) { placeholderText = "ReelBot está pensando..."; } else if (modelIsCurrentlyStreaming) { placeholderText = "ReelBot está escribiendo..."; } else if (selectedFile) { placeholderText = "Añade un comentario sobre el archivo..."; } return (
{/* Added overflow-hidden */}
{/* Adjusted padding for consistency */} {!activeChatSession || activeChatSession.messages.length === 0 ? (
ReelBot Logo

Reel Creator AI

By Jesús Cabrera

Experto en crear Reels virales que convierten visualizaciones en clientes

{SUGGESTION_PROMPTS.map((prompt) => ( handleSuggestionClick(prompt.text)} disabled={isLoading} /> ))}
) : ( activeChatSession.messages.map((msg) => ( )) )}
{error &&
{error}
}
{/* Removed sticky and z-index, handled by App.tsx structure */} {selectedFile && (
{selectedFile.dataUrl && ALLOWED_IMAGE_TYPES.includes(selectedFile.type) ? ( Preview ) : ( )} {selectedFile.name}
)} {fileError &&

{fileError}

}
setUserInput(e.target.value)} placeholder={placeholderText} className="flex-1 p-3 bg-slate-800 border border-slate-700 rounded-lg text-slate-100 placeholder-slate-500 focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500 outline-none transition-shadow duration-150 disabled:opacity-70" disabled={isLoading} /> {isLoading ? ( ) : ( )}
); };