anycoder-804f2c8b / components /ChatWindow.js
Rms666777's picture
Upload components/ChatWindow.js with huggingface_hub
41cae12 verified
import { useState, useRef, useEffect } from 'react';
import { Send, Lock, MoreVertical, Phone, ArrowLeft, ShieldCheck } from 'lucide-react';
import { cryptoUtils } from './EncryptionHelper';
export default function ChatWindow({ chat, onBack, onSendMessage }) {
const [messageText, setMessageText] = useState('');
const [isEncrypting, setIsEncrypting] = useState(false);
const messagesEndRef = useRef(null);
const [localMessages, setLocalMessages] = useState(chat ? chat.messages : []);
// Scroll to bottom on new message
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [localMessages]);
useEffect(() => {
if(chat) setLocalMessages(chat.messages);
}, [chat]);
if (!chat) {
return (
<div className="hidden md:flex flex-1 flex-col items-center justify-center bg-telegram-bg text-telegram-secondary">
<div className="bg-telegram-sidebar p-6 rounded-full mb-4">
<ShieldCheck size={48} className="text-telegram-accent" />
</div>
<h2 className="text-xl font-medium text-white mb-2">SecureChat</h2>
<p className="text-sm max-w-md text-center">
Select a chat to start messaging. <br/>
Messages are protected with end-to-end encryption.
</p>
</div>
);
}
const handleSend = async () => {
if (!messageText.trim()) return;
setIsEncrypting(true);
// Simulate encryption delay
setTimeout(async () => {
// In a real app, we encrypt with the recipient's public key
// Here we use a mock shared secret for the demo
const encryptedContent = await cryptoUtils.encrypt(messageText, "secure-chat-demo-key");
const newMessage = {
id: Date.now(),
text: messageText, // Storing plain text locally for UI demo, but conceptually this is encrypted payload
encryptedPayload: encryptedContent,
sender: 'me',
timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
status: 'sent'
};
setLocalMessages(prev => [...prev, newMessage]);
onSendMessage(chat.id, newMessage);
setMessageText('');
setIsEncrypting(false);
}, 600);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};
return (
<div className="flex-1 flex flex-col bg-[#0e1621] relative h-full w-full">
{/* Chat Header */}
<div className="h-16 bg-telegram-sidebar flex items-center justify-between px-4 border-b border-gray-800 shrink-0">
<div className="flex items-center gap-3">
<button onClick={onBack} className="md:hidden text-telegram-secondary hover:text-white">
<ArrowLeft size={24} />
</button>
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-white font-bold ${chat.isGroup ? 'bg-purple-600' : 'bg-gradient-to-br from-blue-500 to-cyan-400'}`}>
{chat.isGroup ? <span className="text-sm">Grp</span> : chat.name.charAt(0)}
</div>
<div>
<h3 className="font-bold text-white text-sm md:text-base">{chat.name}</h3>
<div className="flex items-center gap-1 text-xs text-telegram-accent">
<Lock size={10} />
<span>Encrypted</span>
</div>
</div>
</div>
<div className="flex items-center gap-4 text-telegram-secondary">
<Phone size={20} className="hover:text-white cursor-pointer hidden sm:block" />
<MoreVertical size={20} className="hover:text-white cursor-pointer" />
</div>
</div>
{/* Messages Area */}
<div className="flex-1 overflow-y-auto p-4 space-y-2 bg-telegram-bg bg-opacity-95">
{/* Encryption Notice */}
<div className="flex justify-center my-4">
<div className="bg-telegram-message/50 text-telegram-secondary text-xs px-3 py-1.5 rounded-lg text-center max-w-xs border border-white/5">
<Lock size={10} className="inline mr-1 mb-0.5" />
Messages are end-to-end encrypted. No one outside of this chat, not even SecureChat, can read or listen to them.
</div>
</div>
{localMessages.map((msg) => (
<div
key={msg.id}
className={`flex ${msg.sender === 'me' ? 'justify-end' : 'justify-start'} animate-fade-in`}
>
<div
className={`message-bubble p-3 rounded-xl text-sm shadow-sm relative ${
msg.sender === 'me'
? 'bg-telegram-outgoing text-white rounded-br-none'
: 'bg-telegram-incoming text-white rounded-bl-none'
}`}
>
<p className="leading-relaxed">{msg.text}</p>
<div className={`flex items-center justify-end gap-1 mt-1 text-[10px] ${msg.sender === 'me' ? 'text-blue-200' : 'text-gray-400'}`}>
<span>{msg.timestamp}</span>
{msg.sender === 'me' && (
<span className={msg.status === 'read' ? 'text-blue-400' : 'text-gray-300'}>
{msg.status === 'read' ? '✓✓' : '✓'}
</span>
)}
</div>
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
{/* Input Area */}
<div className="p-3 bg-telegram-sidebar border-t border-gray-800 shrink-0">
<div className="flex items-end gap-2 max-w-4xl mx-auto">
<button className="p-2 text-telegram-secondary hover:text-white transition-colors">
<MoreVertical size={24} className="rotate-90" />
</button>
<div className="flex-1 bg-telegram-bg rounded-2xl flex items-center border border-transparent focus-within:border-telegram-accent/50 transition-colors">
<textarea
value={messageText}
onChange={(e) => setMessageText(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Message"
className="w-full bg-transparent text-white px-4 py-3 min-h-[44px] max-h-32 focus:outline-none resize-none text-sm"
rows={1}
/>
</div>
<button
onClick={handleSend}
disabled={!messageText.trim() || isEncrypting}
className={`p-3 rounded-full transition-all duration-200 flex items-center justify-center ${
messageText.trim()
? 'bg-telegram-accent text-white hover:bg-blue-600 shadow-lg shadow-blue-900/20'
: 'bg-telegram-bg text-telegram-secondary'
}`}
>
{isEncrypting ? (
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : (
<Send size={20} className={messageText.trim() ? 'ml-0.5' : ''} />
)}
</button>
</div>
</div>
</div>
);
}