AeThex-Connect/src/frontend/mockup/ChatArea.jsx

283 lines
10 KiB
JavaScript

import React, { useState, useRef, useEffect } from 'react';
import MessageActions from './MessageActions';
import TypingIndicator from './TypingIndicator';
import EmojiPicker from './EmojiPicker';
const initialMessages = [
{
type: 'system',
division: 'foundation',
label: '[FOUNDATION] System Announcement',
text: 'Foundation authentication services upgraded to v2.1.0. Enhanced security protocols now active across all AeThex infrastructure.',
},
{
type: 'message',
id: 1,
author: 'Trevor',
badge: 'foundation',
badgeLabel: 'Foundation',
time: '10:34 AM',
avatar: { initial: 'T', gradient: 'linear-gradient(135deg, #ff0000, #cc0000)' },
text: 'Just pushed the authentication updates. All services should automatically migrate to the new protocols within 24 hours.',
reactions: [{ emoji: '🔥', count: 3, reacted: true }, { emoji: '👍', count: 2, reacted: false }],
},
{
type: 'message',
id: 2,
author: 'Marcus',
time: '10:41 AM',
avatar: { initial: 'M', gradient: 'linear-gradient(135deg, #0066ff, #003380)' },
text: "Excellent work! I've been testing the new Passport integration and it's incredibly smooth. The Trinity color-coding in the UI makes it really clear which division is handling what.",
},
{
type: 'system',
division: 'labs',
label: '[LABS] Experimental Feature Alert',
text: 'Nexus Engine v2.0-beta now available for testing. New cross-platform sync reduces latency by 40%. Join #labs-testing to participate.',
},
{
type: 'message',
id: 3,
author: 'Sarah',
badge: 'labs',
badgeLabel: 'Labs',
time: '11:15 AM',
avatar: { initial: 'S', gradient: 'linear-gradient(135deg, #ffa500, #ff8c00)' },
text: 'The Nexus v2 parallel compilation is insane. Cut my build time from 3 minutes to under 2. Still some edge cases with complex state synchronization but wow...',
reactions: [{ emoji: '🚀', count: 5, reacted: true }],
},
{
type: 'message',
id: 4,
author: 'Anderson',
badge: 'foundation',
badgeLabel: 'Founder',
time: '11:47 AM',
avatar: { initial: 'A', gradient: 'linear-gradient(135deg, #ff0000, #0066ff, #ffa500)' },
text: 'Love seeing the Trinity infrastructure working in harmony. Foundation keeping everything secure, Labs pushing the boundaries, Corporation delivering production-ready tools. This is exactly the vision.',
reactions: [{ emoji: '❤️', count: 8, reacted: false }, { emoji: '🔥', count: 4, reacted: true }],
},
{
type: 'message',
id: 5,
author: 'DevUser_2847',
time: '12:03 PM',
avatar: { initial: 'D' },
text: 'Quick question - when using AeThex Studio, does the Terminal automatically connect to all three Trinity divisions, or do I need to configure that?',
},
{
type: 'system',
division: 'corporation',
label: '[CORPORATION] Service Update',
text: 'AeThex Studio Pro users: New Railway deployment templates available. Optimized configurations for Foundation APIs, Corporation services, and Labs experiments.',
},
];
export default function ChatArea({ onOpenSearch, onOpenThread, onPinnedClick, onNotificationsClick, onContextMenu }) {
const [messages, setMessages] = useState(initialMessages);
const [inputValue, setInputValue] = useState('');
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const [hoveredMessage, setHoveredMessage] = useState(null);
const [typingUsers] = useState(['Sarah', 'Marcus']); // Simulated typing
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSend = () => {
if (!inputValue.trim()) return;
const newMessage = {
type: 'message',
id: Date.now(),
author: 'You',
time: new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }),
avatar: { initial: 'Y', gradient: 'linear-gradient(135deg, #0066ff, #003380)' },
text: inputValue,
};
setMessages([...messages, newMessage]);
setInputValue('');
};
const handleKeyPress = (e) => {
if (e.key === 'Enter') {
handleSend();
}
};
const handleEmojiSelect = (emoji) => {
setInputValue((prev) => prev + emoji);
setShowEmojiPicker(false);
};
const handleReaction = (messageId, emoji) => {
setMessages((prev) =>
prev.map((msg) => {
if (msg.id !== messageId) return msg;
const reactions = msg.reactions || [];
const existing = reactions.find((r) => r.emoji === emoji);
if (existing) {
return {
...msg,
reactions: reactions.map((r) =>
r.emoji === emoji
? { ...r, count: r.reacted ? r.count - 1 : r.count + 1, reacted: !r.reacted }
: r
).filter((r) => r.count > 0),
};
}
return {
...msg,
reactions: [...reactions, { emoji, count: 1, reacted: true }],
};
})
);
};
return (
<div className="chat-area">
<div className="chat-header">
<span className="channel-name-header"># general</span>
<div className="chat-tools">
<span
className="chat-tool"
onClick={onNotificationsClick}
style={{ cursor: 'pointer' }}
title="Notifications"
>
🔔
</span>
<span
className="chat-tool"
onClick={onPinnedClick}
style={{ cursor: 'pointer' }}
title="Pinned Messages"
>
📌
</span>
<span className="chat-tool">👥 128</span>
<span className="chat-tool" onClick={onOpenSearch} style={{ cursor: 'pointer' }} title="Search">🔍</span>
</div>
</div>
<div className="chat-messages">
{messages.map((msg, idx) => {
if (msg.type === 'system') {
return (
<div key={`system-${idx}`} className={`message-system ${msg.division}`}>
<div className={`system-label ${msg.division}`}>{msg.label}</div>
<div>{msg.text}</div>
</div>
);
}
return (
<div
key={msg.id}
className="message"
onMouseEnter={() => setHoveredMessage(msg.id)}
onMouseLeave={() => setHoveredMessage(null)}
>
<div
className="message-avatar"
style={msg.avatar.gradient ? { background: msg.avatar.gradient } : undefined}
>
{msg.avatar.initial}
</div>
<div className="message-content">
<div className="message-header">
<span className="message-author">{msg.author}</span>
{msg.badge && (
<span className={`message-badge ${msg.badge}`}>{msg.badgeLabel}</span>
)}
<span className="message-time">{msg.time}</span>
</div>
<div className="message-text">{msg.text}</div>
{/* Reactions */}
{msg.reactions && msg.reactions.length > 0 && (
<div className="message-reactions" style={{ display: 'flex', gap: '6px', marginTop: '6px' }}>
{msg.reactions.map((r) => (
<button
key={r.emoji}
onClick={() => handleReaction(msg.id, r.emoji)}
style={{
display: 'flex',
alignItems: 'center',
gap: '4px',
padding: '2px 8px',
background: r.reacted ? 'rgba(88, 101, 242, 0.3)' : '#2f3136',
border: r.reacted ? '1px solid #5865f2' : '1px solid transparent',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '0.85em',
}}
>
<span>{r.emoji}</span>
<span style={{ color: r.reacted ? '#5865f2' : '#b9bbbe' }}>{r.count}</span>
</button>
))}
</div>
)}
</div>
{/* Message Actions on Hover */}
{hoveredMessage === msg.id && (
<MessageActions
onReact={(emoji) => handleReaction(msg.id, emoji)}
onReply={() => onOpenThread && onOpenThread(msg)}
onThread={() => onOpenThread && onOpenThread(msg)}
isOwn={msg.author === 'You'}
/>
)}
</div>
);
})}
<div ref={messagesEndRef} />
</div>
{/* Typing Indicator */}
{typingUsers.length > 0 && <TypingIndicator users={typingUsers} />}
<div className="message-input-container" style={{ position: 'relative' }}>
<button
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
style={{
position: 'absolute',
right: '12px',
top: '50%',
transform: 'translateY(-50%)',
background: 'none',
border: 'none',
cursor: 'pointer',
fontSize: '1.2em',
zIndex: 10,
}}
>
😊
</button>
<input
type="text"
className="message-input"
placeholder="Message #general (Foundation infrastructure channel)"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={handleKeyPress}
style={{ paddingRight: '48px' }}
/>
{showEmojiPicker && (
<EmojiPicker
onSelect={handleEmojiSelect}
onClose={() => setShowEmojiPicker(false)}
/>
)}
</div>
</div>
);
}