Files
OpenLearnX/chatApp/account.html
2025-09-14 16:59:36 +05:30

733 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlockChat</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', sans-serif;
background: #0e1621;
height: 100vh;
overflow: hidden;
color: white;
}
.chat-container {
display: flex;
height: 100vh;
}
/* Sidebar */
.sidebar {
width: 400px;
background: #1e2129;
border-right: 1px solid #2e3a59;
display: flex;
flex-direction: column;
}
.sidebar-header {
background: linear-gradient(135deg, #667eea, #764ba2);
padding: 25px;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
}
.user-info h4 {
margin: 0;
font-size: 20px;
}
.user-info small {
opacity: 0.9;
}
.contacts-list {
flex: 1;
overflow-y: auto;
padding: 10px 0;
}
.contact-item {
padding: 20px 25px;
cursor: pointer;
display: flex;
align-items: center;
transition: all 0.3s;
color: #8e959e;
border-left: 3px solid transparent;
}
.contact-item:hover {
background: #2e3a59;
color: white;
}
.contact-item.active {
background: #2e3a59;
border-left-color: #667eea;
color: white;
}
.contact-avatar {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
margin-right: 15px;
}
.contact-info h6 {
margin: 0 0 5px 0;
font-size: 16px;
}
.contact-info small {
opacity: 0.7;
}
/* Chat Area */
.chat-area {
flex: 1;
display: flex;
flex-direction: column;
}
.chat-header {
background: #1e2129;
padding: 20px 25px;
border-bottom: 1px solid #2e3a59;
display: flex;
align-items: center;
}
.chat-avatar {
width: 45px;
height: 45px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-weight: bold;
}
.messages-area {
flex: 1;
padding: 20px;
overflow-y: auto;
background: #0e1621;
}
.message {
margin-bottom: 15px;
display: flex;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message.sent {
justify-content: flex-end;
}
.message.received {
justify-content: flex-start;
}
.message-bubble {
max-width: 70%;
padding: 12px 16px;
border-radius: 18px;
word-wrap: break-word;
}
.message.sent .message-bubble {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-bottom-right-radius: 4px;
}
.message.received .message-bubble {
background: #1e2129;
color: white;
border-bottom-left-radius: 4px;
}
.message-time {
font-size: 11px;
opacity: 0.7;
margin-top: 5px;
}
.input-area {
background: #1e2129;
padding: 20px 25px;
border-top: 1px solid #2e3a59;
display: flex;
gap: 15px;
align-items: center;
}
.message-input {
flex: 1;
background: #0e1621;
border: 2px solid #2e3a59;
border-radius: 25px;
padding: 12px 20px;
color: white;
outline: none;
font-size: 15px;
}
.message-input:focus {
border-color: #667eea;
}
.message-input::placeholder {
color: #8e959e;
}
.send-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
}
.send-btn:hover:not(:disabled) {
transform: scale(1.1);
}
.send-btn:disabled {
background: #666;
cursor: not-allowed;
}
.welcome-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: #8e959e;
}
.welcome-screen i {
font-size: 80px;
margin-bottom: 20px;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.empty-state {
text-align: center;
color: #8e959e;
padding: 50px 20px;
}
.empty-state i {
font-size: 60px;
margin-bottom: 15px;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>
</head>
<body>
<div class="chat-container">
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-header">
<div class="user-info">
<h4><i class="fas fa-comments me-2"></i>BlockChat</h4>
<small id="myAddress">Loading...</small>
</div>
<button class="btn btn-outline-light btn-sm" onclick="logout()">
<i class="fas fa-sign-out-alt"></i>
</button>
</div>
<div class="contacts-list" id="contactsList">
<div class="text-center p-4">
<div class="spinner-border text-primary"></div>
<p class="mt-2">Loading contacts...</p>
</div>
</div>
</div>
<!-- Chat Area -->
<div class="chat-area">
<div class="welcome-screen">
<i class="fas fa-comments"></i>
<h3>Welcome to BlockChat</h3>
<p>Select a contact to start messaging</p>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.7.4/web3.min.js"></script>
<script>
// Global variables
let myAccount = localStorage.getItem('myaddress');
let web3, contract;
let currentChat = null;
let allContacts = [];
let messageStore = new Map(); // Store messages for each chat
let blockchainCheckTimer;
const CONTRACT_ADDRESS = '0xd9145CCE52D386f254917e481eB44e9943F39138';
const CONTRACT_ABI = [
{
"anonymous": false,
"inputs": [
{"indexed": true, "internalType": "address", "name": "from", "type": "address"},
{"indexed": false, "internalType": "string", "name": "message", "type": "string"},
{"indexed": false, "internalType": "address", "name": "to", "type": "address"},
{"indexed": false, "internalType": "string", "name": "timestamp", "type": "string"}
],
"name": "message",
"type": "event"
},
{
"inputs": [
{"internalType": "address", "name": "_to", "type": "address"},
{"internalType": "string", "name": "_message", "type": "string"},
{"internalType": "string", "name": "time", "type": "string"}
],
"name": "sendMessage",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];
// Initialize app - NO REFRESH
window.addEventListener('load', async function() {
if (!myAccount) {
alert('Please login first!');
window.location.href = 'index.html';
return;
}
console.log('🚀 Starting BlockChat (NO REFRESH MODE)');
await initializeApp();
});
async function initializeApp() {
try {
// Connect to blockchain
web3 = new Web3('http://127.0.0.1:8545');
contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);
// Test connection
await web3.eth.net.isListening();
console.log('✅ Connected to blockchain');
// Show user address
document.getElementById('myAddress').textContent =
myAccount.substring(0, 6) + '...' + myAccount.substring(38);
// Load contacts
await loadContacts();
// Load all existing messages ONCE
await loadAllExistingMessages();
// Start checking for NEW messages only (every 6 seconds)
startNewMessageChecker();
} catch (error) {
console.error('❌ Initialization failed:', error);
alert('Failed to connect to blockchain!');
}
}
async function loadContacts() {
try {
const accounts = await web3.eth.getAccounts();
allContacts = accounts.filter(acc => acc.toLowerCase() !== myAccount.toLowerCase());
let html = '';
allContacts.forEach((contact, index) => {
html += `
<div class="contact-item" onclick="openChat('${contact}', ${index})">
<div class="contact-avatar">${index + 1}</div>
<div class="contact-info">
<h6>Contact ${index + 1}</h6>
<small>${contact.substring(0, 10)}...${contact.substring(32)}</small>
</div>
</div>
`;
});
document.getElementById('contactsList').innerHTML = html;
console.log('📞 Loaded', allContacts.length, 'contacts');
} catch (error) {
console.error('❌ Error loading contacts:', error);
}
}
// Load all existing messages ONCE when app starts
async function loadAllExistingMessages() {
try {
console.log('📨 Loading all existing messages...');
const events = await contract.getPastEvents('message', {
fromBlock: 0,
toBlock: 'latest'
});
console.log(`Found ${events.length} existing messages on blockchain`);
// Store messages by chat address
events.forEach(event => {
const from = event.returnValues.from.toLowerCase();
const to = event.returnValues.to.toLowerCase();
const message = event.returnValues.message;
const timestamp = event.returnValues.timestamp;
const myAddr = myAccount.toLowerCase();
// Determine which chat this message belongs to
let chatWith = null;
let isMyMessage = false;
if (from === myAddr) {
chatWith = to;
isMyMessage = true;
} else if (to === myAddr) {
chatWith = from;
isMyMessage = false;
}
if (chatWith) {
if (!messageStore.has(chatWith)) {
messageStore.set(chatWith, []);
}
messageStore.get(chatWith).push({
message: message,
timestamp: timestamp,
isMyMessage: isMyMessage,
blockNumber: event.blockNumber,
id: `${from}-${to}-${event.blockNumber}-${event.transactionHash}`
});
}
});
// Sort all message arrays by block number
messageStore.forEach(messages => {
messages.sort((a, b) => a.blockNumber - b.blockNumber);
});
console.log('💾 Stored messages for', messageStore.size, 'conversations');
} catch (error) {
console.error('❌ Error loading existing messages:', error);
}
}
function openChat(contactAddress, index) {
currentChat = contactAddress.toLowerCase();
console.log('💬 Opening chat with:', currentChat);
// Update contact selection
document.querySelectorAll('.contact-item').forEach(item => item.classList.remove('active'));
document.querySelectorAll('.contact-item')[index].classList.add('active');
// Show chat interface
showChatInterface(contactAddress, index + 1);
// Display messages for this chat (from memory - NO BLOCKCHAIN CALL)
displayStoredMessages(currentChat);
}
function showChatInterface(contactAddress, contactNumber) {
const chatArea = document.querySelector('.chat-area');
chatArea.innerHTML = `
<div class="chat-header">
<div class="chat-avatar">${contactNumber}</div>
<div>
<h6 style="margin: 0;">Contact ${contactNumber}</h6>
<small>${contactAddress.substring(0, 12)}...</small>
</div>
</div>
<div class="messages-area" id="messagesArea">
</div>
<div class="input-area">
<input type="text" class="message-input" id="messageInput"
placeholder="Type a message..." onkeypress="handleEnter(event)">
<button class="send-btn" onclick="sendMessage()" id="sendBtn">
<i class="fas fa-paper-plane"></i>
</button>
</div>
`;
}
// Display messages from memory (INSTANT - NO BLOCKCHAIN CALL)
function displayStoredMessages(chatAddress) {
const messagesArea = document.getElementById('messagesArea');
if (!messagesArea) return;
const messages = messageStore.get(chatAddress) || [];
console.log(`💬 Displaying ${messages.length} stored messages for ${chatAddress}`);
if (messages.length === 0) {
messagesArea.innerHTML = `
<div class="empty-state">
<i class="fas fa-comment-dots"></i>
<h5>Start Conversation</h5>
<p>Send the first message!</p>
</div>
`;
return;
}
let html = '';
messages.forEach(msg => {
const messageClass = msg.isMyMessage ? 'sent' : 'received';
const timeLabel = msg.isMyMessage ? 'sent' : 'received';
html += `
<div class="message ${messageClass}">
<div class="message-bubble">
<div>${escapeHtml(msg.message)}</div>
<div class="message-time">${timeLabel} ${msg.timestamp}</div>
</div>
</div>
`;
});
messagesArea.innerHTML = html;
messagesArea.scrollTop = messagesArea.scrollHeight;
}
// INSTANT message adding to UI
function addMessageToUI(chatAddress, message, timestamp, isMyMessage) {
console.log(' Adding message to UI instantly:', message);
// Add to memory store
if (!messageStore.has(chatAddress)) {
messageStore.set(chatAddress, []);
}
messageStore.get(chatAddress).push({
message: message,
timestamp: timestamp,
isMyMessage: isMyMessage,
blockNumber: 999999, // Temporary
id: `temp-${Date.now()}`
});
// Update UI if this chat is currently open
if (currentChat === chatAddress) {
displayStoredMessages(chatAddress);
}
}
async function sendMessage() {
if (!currentChat) {
alert('Please select a chat first!');
return;
}
const input = document.getElementById('messageInput');
const sendBtn = document.getElementById('sendBtn');
const messageText = input.value.trim();
if (!messageText) {
alert('Please enter a message!');
return;
}
try {
console.log('📤 Sending message:', messageText);
// Create timestamp
const now = new Date();
const timestamp = now.toLocaleDateString("en-IN") + ' ' +
now.toLocaleTimeString("en-IN", {
hour12: true,
hour: '2-digit',
minute: '2-digit'
});
// INSTANTLY show message in UI
addMessageToUI(currentChat, messageText, timestamp, true);
// Clear input immediately
input.value = '';
// Disable send button temporarily
sendBtn.disabled = true;
sendBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
// Send to blockchain in background
await contract.methods
.sendMessage(currentChat, messageText, timestamp)
.send({
from: myAccount,
gas: 300000
});
console.log('✅ Message sent to blockchain');
} catch (error) {
console.error('❌ Failed to send message:', error);
alert('Failed to send message: ' + error.message);
// Remove the temporary message from UI
const messages = messageStore.get(currentChat);
if (messages) {
const tempIndex = messages.findIndex(msg => msg.id.startsWith('temp-'));
if (tempIndex !== -1) {
messages.splice(tempIndex, 1);
displayStoredMessages(currentChat);
}
}
} finally {
// Re-enable send button
sendBtn.disabled = false;
sendBtn.innerHTML = '<i class="fas fa-paper-plane"></i>';
input.focus();
}
}
// Check for NEW messages only (every 6 seconds)
function startNewMessageChecker() {
let lastCheckedBlock = 0;
// Get current block number
web3.eth.getBlockNumber().then(blockNumber => {
lastCheckedBlock = blockNumber;
console.log('📦 Starting from block:', lastCheckedBlock);
});
blockchainCheckTimer = setInterval(async () => {
try {
const currentBlock = await web3.eth.getBlockNumber();
if (currentBlock > lastCheckedBlock) {
console.log('📦 Checking for new messages from block', lastCheckedBlock + 1, 'to', currentBlock);
// Only get events from new blocks
const newEvents = await contract.getPastEvents('message', {
fromBlock: lastCheckedBlock + 1,
toBlock: currentBlock
});
if (newEvents.length > 0) {
console.log('📨 Found', newEvents.length, 'new messages');
// Process new messages
newEvents.forEach(event => {
const from = event.returnValues.from.toLowerCase();
const to = event.returnValues.to.toLowerCase();
const message = event.returnValues.message;
const timestamp = event.returnValues.timestamp;
const myAddr = myAccount.toLowerCase();
// Only process if it involves current user
if (from === myAddr || to === myAddr) {
let chatWith = from === myAddr ? to : from;
let isMyMessage = from === myAddr;
// Add to message store
if (!messageStore.has(chatWith)) {
messageStore.set(chatWith, []);
}
// Remove any temporary messages with same content
const messages = messageStore.get(chatWith);
const tempIndex = messages.findIndex(msg =>
msg.id.startsWith('temp-') && msg.message === message
);
if (tempIndex !== -1) {
messages.splice(tempIndex, 1);
}
// Add the real message
messages.push({
message: message,
timestamp: timestamp,
isMyMessage: isMyMessage,
blockNumber: event.blockNumber,
id: `${from}-${to}-${event.blockNumber}-${event.transactionHash}`
});
// Sort by block number
messages.sort((a, b) => a.blockNumber - b.blockNumber);
// Update UI if this chat is currently open
if (currentChat === chatWith) {
console.log('🔄 Updating current chat display');
displayStoredMessages(chatWith);
}
}
});
}
lastCheckedBlock = currentBlock;
}
} catch (error) {
console.error('❌ Error checking for new messages:', error);
}
}, 6000); // Check every 6 seconds
}
function handleEnter(event) {
if (event.key === 'Enter') {
event.preventDefault();
sendMessage();
}
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function logout() {
if (blockchainCheckTimer) clearInterval(blockchainCheckTimer);
localStorage.removeItem('myaddress');
window.location.href = 'index.html';
}
// Cleanup on page unload
window.addEventListener('beforeunload', function() {
if (blockchainCheckTimer) clearInterval(blockchainCheckTimer);
});
</script>
</body>
</html>