Files
LocalAGI/webui/react-ui/src/pages/Chat.jsx
Ettore Di Giacinto e7111c6554 Rename
2025-04-08 22:18:32 +02:00

150 lines
4.4 KiB
JavaScript

import { useState, useRef, useEffect } from 'react';
import { useParams, useOutletContext, useNavigate } from 'react-router-dom';
import { useChat } from '../hooks/useChat';
function Chat() {
const { name } = useParams();
const { showToast } = useOutletContext();
const navigate = useNavigate();
const [message, setMessage] = useState('');
const messagesEndRef = useRef(null);
// Use our custom chat hook
const {
messages,
sending,
error,
isConnected,
sendMessage,
clearChat,
clearError
} = useChat(name);
// Update document title
useEffect(() => {
if (name) {
document.title = `Chat with ${name} - LocalAGI`;
}
return () => {
document.title = 'LocalAGI'; // Reset title when component unmounts
};
}, [name]);
// Scroll to bottom when messages change
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
// Show error toast if there's an error
useEffect(() => {
if (error) {
showToast(error, 'error');
clearError();
}
}, [error, showToast, clearError]);
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
if (!message.trim()) return;
const success = await sendMessage(message.trim());
if (success) {
setMessage('');
}
};
// Handle pressing Enter to send (Shift+Enter for new line)
const handleKeyDown = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit(e);
}
};
return (
<div className="agents-container">
<header className="page-header">
<h1>Chat with {name}</h1>
<div className="connection-status" style={{ display: 'flex', alignItems: 'center' }}>
<span
className={isConnected ? 'active' : 'inactive'}
style={{
position: 'static',
display: 'inline-block',
padding: '5px 12px',
borderRadius: '20px',
fontSize: '0.8rem',
fontWeight: '500',
textTransform: 'uppercase',
letterSpacing: '1px',
boxShadow: '0 0 10px rgba(0, 0, 0, 0.2)',
marginLeft: '10px'
}}
>
{isConnected ? 'Connected' : 'Disconnected'}
</span>
</div>
<button
className="action-btn delete-btn"
onClick={clearChat}
disabled={messages.length === 0}
>
<i className="fas fa-trash-alt"></i> Clear Chat
</button>
</header>
<div className="chat-container">
<div className="chat-messages">
{messages.length === 0 ? (
<div className="no-agents">
<h2>No messages yet</h2>
<p>Start a conversation with {name}!</p>
</div>
) : (
messages.map((msg) => (
<div
key={msg.id}
className={`message ${msg.sender === 'user' ? 'message-user' : 'message-agent'}`}
>
<div className="message-content">
{msg.content}
</div>
<div className="message-timestamp">
{new Date(msg.timestamp).toLocaleTimeString()}
</div>
</div>
))
)}
<div ref={messagesEndRef} />
</div>
<div className="chat-input">
<form className="message-form" onSubmit={handleSubmit} style={{ display: 'flex', gap: '1rem', width: '100%' }}>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Type your message... (Press Enter to send, Shift+Enter for new line)"
disabled={sending || !isConnected}
className="form-control"
rows={5}
style={{ flex: 1, resize: 'vertical', minHeight: '38px', maxHeight: '150px' }}
/>
<button
type="submit"
disabled={sending || !message.trim() || !isConnected}
className="action-btn chat-btn"
style={{ alignSelf: 'flex-end' }}
>
<i className={`fas ${sending ? 'fa-spinner fa-spin' : 'fa-paper-plane'}`}></i> {sending ? 'Sending...' : 'Send'}
</button>
</form>
</div>
</div>
</div>
);
}
export default Chat;