AeThex-Connect/src/frontend/components/Chat/MessageInput.jsx
MrPiglr cad2e81fc4
Phase 2: Complete Messaging System Implementation
- Added real-time messaging with Socket.io
- Created comprehensive database schema (8 tables, functions, triggers)
- Implemented messaging service with full CRUD operations
- Built Socket.io service for real-time communication
- Created React messaging components (Chat, ConversationList, MessageList, MessageInput)
- Added end-to-end encryption utilities (RSA + AES-256-GCM)
- Implemented 16 RESTful API endpoints
- Added typing indicators, presence tracking, reactions
- Created modern, responsive UI with animations
- Updated server with Socket.io integration
- Fixed auth middleware imports
- Added comprehensive documentation

Features:
- Direct and group conversations
- Real-time message delivery
- Message editing and deletion
- Emoji reactions
- Typing indicators
- Online/offline presence
- Read receipts
- User search
- File attachment support (endpoint ready)
- Client-side encryption utilities

Dependencies:
- socket.io ^4.7.5
- socket.io-client ^4.7.5
2026-01-10 04:45:07 +00:00

134 lines
3 KiB
JavaScript

/**
* MessageInput Component
* Input field for sending messages
*/
import React, { useState, useRef } from 'react';
import './MessageInput.css';
export default function MessageInput({ onSend, onTyping, onStopTyping }) {
const [message, setMessage] = useState('');
const [uploading, setUploading] = useState(false);
const fileInputRef = useRef(null);
const typingTimeoutRef = useRef(null);
const handleChange = (e) => {
setMessage(e.target.value);
// Trigger typing indicator
if (onTyping) onTyping();
// Reset stop-typing timeout
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}
typingTimeoutRef.current = setTimeout(() => {
if (onStopTyping) onStopTyping();
}, 1000);
};
const handleSubmit = (e) => {
e.preventDefault();
if (!message.trim()) return;
onSend(message);
setMessage('');
if (onStopTyping) onStopTyping();
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}
};
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit(e);
}
};
const handleFileUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
setUploading(true);
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(
`${import.meta.env.VITE_API_URL || 'http://localhost:3000'}/api/files/upload`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: formData
}
);
const data = await response.json();
if (data.success) {
// Send message with file attachment
onSend(`📎 ${file.name}`, [data.file]);
}
} catch (error) {
console.error('File upload failed:', error);
alert('Failed to upload file');
} finally {
setUploading(false);
}
};
return (
<form className="message-input" onSubmit={handleSubmit}>
<button
type="button"
className="btn-attach"
onClick={() => fileInputRef.current?.click()}
disabled={uploading}
title="Attach file"
>
{uploading ? '⏳' : '📎'}
</button>
<input
type="file"
ref={fileInputRef}
onChange={handleFileUpload}
style={{ display: 'none' }}
/>
<textarea
value={message}
onChange={handleChange}
onKeyDown={handleKeyPress}
placeholder="Type a message..."
rows={1}
disabled={uploading}
className="message-textarea"
/>
<button
type="button"
className="btn-emoji"
title="Add emoji"
>
😊
</button>
<button
type="submit"
className="btn-send"
disabled={!message.trim() || uploading}
>
Send
</button>
</form>
);
}