Hackathon 14/sep/2025 Final

Signed-off-by: 5t4l1n <stalin78830@gmail.com>
This commit is contained in:
2025-09-14 16:59:36 +05:30
parent df420e8331
commit 5c416361b4
2 changed files with 686 additions and 199 deletions
+226 -97
View File
@@ -135,6 +135,12 @@
.message { .message {
margin-bottom: 15px; margin-bottom: 15px;
display: flex; display: flex;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
} }
.message.sent { .message.sent {
@@ -247,7 +253,7 @@
.empty-state i { .empty-state i {
font-size: 60px; font-size: 60px;
margin-bottom: 15px; margin-bottom: 15px;
background: linear-gradient(135d deg, #667eea, #764ba2); background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
@@ -292,6 +298,8 @@
let web3, contract; let web3, contract;
let currentChat = null; let currentChat = null;
let allContacts = []; let allContacts = [];
let messageStore = new Map(); // Store messages for each chat
let blockchainCheckTimer;
const CONTRACT_ADDRESS = '0xd9145CCE52D386f254917e481eB44e9943F39138'; const CONTRACT_ADDRESS = '0xd9145CCE52D386f254917e481eB44e9943F39138';
const CONTRACT_ABI = [ const CONTRACT_ABI = [
@@ -319,16 +327,16 @@
} }
]; ];
// Initialize app // Initialize app - NO REFRESH
window.addEventListener('load', function() { window.addEventListener('load', async function() {
if (!myAccount) { if (!myAccount) {
alert('Please login first!'); alert('Please login first!');
window.location.href = 'index.html'; window.location.href = 'index.html';
return; return;
} }
console.log('🚀 Starting BlockChat for:', myAccount); console.log('🚀 Starting BlockChat (NO REFRESH MODE)');
initializeApp(); await initializeApp();
}); });
async function initializeApp() { async function initializeApp() {
@@ -348,8 +356,11 @@
// Load contacts // Load contacts
await loadContacts(); await loadContacts();
// Start monitoring for new messages // Load all existing messages ONCE
startMessageMonitoring(); await loadAllExistingMessages();
// Start checking for NEW messages only (every 6 seconds)
startNewMessageChecker();
} catch (error) { } catch (error) {
console.error('❌ Initialization failed:', error); console.error('❌ Initialization failed:', error);
@@ -383,9 +394,68 @@
} }
} }
// 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) { function openChat(contactAddress, index) {
currentChat = contactAddress; currentChat = contactAddress.toLowerCase();
console.log('💬 Opening chat with:', contactAddress); console.log('💬 Opening chat with:', currentChat);
// Update contact selection // Update contact selection
document.querySelectorAll('.contact-item').forEach(item => item.classList.remove('active')); document.querySelectorAll('.contact-item').forEach(item => item.classList.remove('active'));
@@ -394,8 +464,8 @@
// Show chat interface // Show chat interface
showChatInterface(contactAddress, index + 1); showChatInterface(contactAddress, index + 1);
// Load messages for this chat // Display messages for this chat (from memory - NO BLOCKCHAIN CALL)
loadMessagesForChat(contactAddress); displayStoredMessages(currentChat);
} }
function showChatInterface(contactAddress, contactNumber) { function showChatInterface(contactAddress, contactNumber) {
@@ -410,11 +480,6 @@
</div> </div>
<div class="messages-area" id="messagesArea"> <div class="messages-area" id="messagesArea">
<div class="empty-state">
<i class="fas fa-comment-dots"></i>
<h5>Start Conversation</h5>
<p>Send the first message!</p>
</div>
</div> </div>
<div class="input-area"> <div class="input-area">
@@ -427,56 +492,14 @@
`; `;
} }
async function loadMessagesForChat(contactAddress) { // Display messages from memory (INSTANT - NO BLOCKCHAIN CALL)
try { function displayStoredMessages(chatAddress) {
console.log('📨 Loading messages for:', contactAddress);
const events = await contract.getPastEvents('message', {
fromBlock: 0,
toBlock: 'latest'
});
console.log(`Found ${events.length} total events on blockchain`);
// Filter messages for this specific chat
const chatMessages = [];
const myAddr = myAccount.toLowerCase();
const contactAddr = contactAddress.toLowerCase();
events.forEach(event => {
const from = event.returnValues.from.toLowerCase();
const to = event.returnValues.to.toLowerCase();
// Check if this message belongs to our conversation
if ((from === myAddr && to === contactAddr) || (from === contactAddr && to === myAddr)) {
chatMessages.push({
from: from,
to: to,
message: event.returnValues.message,
timestamp: event.returnValues.timestamp,
blockNumber: event.blockNumber,
isMyMessage: from === myAddr
});
}
});
// Sort by block number
chatMessages.sort((a, b) => a.blockNumber - b.blockNumber);
console.log(`💬 Found ${chatMessages.length} messages in this chat`);
// Display messages
displayMessages(chatMessages);
} catch (error) {
console.error('❌ Error loading messages:', error);
}
}
function displayMessages(messages) {
const messagesArea = document.getElementById('messagesArea'); const messagesArea = document.getElementById('messagesArea');
if (!messagesArea) return; if (!messagesArea) return;
const messages = messageStore.get(chatAddress) || [];
console.log(`💬 Displaying ${messages.length} stored messages for ${chatAddress}`);
if (messages.length === 0) { if (messages.length === 0) {
messagesArea.innerHTML = ` messagesArea.innerHTML = `
<div class="empty-state"> <div class="empty-state">
@@ -491,22 +514,43 @@
let html = ''; let html = '';
messages.forEach(msg => { messages.forEach(msg => {
const messageClass = msg.isMyMessage ? 'sent' : 'received'; const messageClass = msg.isMyMessage ? 'sent' : 'received';
const timeLabel = msg.isMyMessage ? 'sent' : 'received';
html += ` html += `
<div class="message ${messageClass}"> <div class="message ${messageClass}">
<div class="message-bubble"> <div class="message-bubble">
<div>${escapeHtml(msg.message)}</div> <div>${escapeHtml(msg.message)}</div>
<div class="message-time">${msg.timestamp}</div> <div class="message-time">${timeLabel} ${msg.timestamp}</div>
</div> </div>
</div> </div>
`; `;
}); });
messagesArea.innerHTML = html; 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);
// Scroll to bottom // Add to memory store
setTimeout(() => { if (!messageStore.has(chatAddress)) {
messagesArea.scrollTop = messagesArea.scrollHeight; messageStore.set(chatAddress, []);
}, 100); }
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() { async function sendMessage() {
@@ -525,12 +569,7 @@
} }
try { try {
console.log('📤 Sending message:', messageText, 'to:', currentChat); console.log('📤 Sending message:', messageText);
// Disable UI
input.disabled = true;
sendBtn.disabled = true;
sendBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
// Create timestamp // Create timestamp
const now = new Date(); const now = new Date();
@@ -541,36 +580,130 @@
minute: '2-digit' minute: '2-digit'
}); });
// Send to blockchain // INSTANTLY show message in UI
const receipt = await contract.methods 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) .sendMessage(currentChat, messageText, timestamp)
.send({ .send({
from: myAccount, from: myAccount,
gas: 300000 gas: 300000
}); });
console.log('✅ Message sent! Transaction:', receipt.transactionHash); console.log('✅ Message sent to blockchain');
// Clear input
input.value = '';
// Reload messages to show the new one
setTimeout(() => {
loadMessagesForChat(currentChat);
}, 2000);
} catch (error) { } catch (error) {
console.error('❌ Failed to send message:', error); console.error('❌ Failed to send message:', error);
alert('Failed to send message: ' + error.message); 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 { } finally {
// Re-enable UI // Re-enable send button
input.disabled = false;
sendBtn.disabled = false; sendBtn.disabled = false;
sendBtn.innerHTML = '<i class="fas fa-paper-plane"></i>'; sendBtn.innerHTML = '<i class="fas fa-paper-plane"></i>';
input.focus(); 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) { function handleEnter(event) {
if (event.key === 'Enter') { if (event.key === 'Enter') {
event.preventDefault(); event.preventDefault();
@@ -578,16 +711,6 @@
} }
} }
// Monitor for new messages every 5 seconds
function startMessageMonitoring() {
setInterval(() => {
if (currentChat) {
console.log('🔄 Checking for new messages...');
loadMessagesForChat(currentChat);
}
}, 5000);
}
function escapeHtml(text) { function escapeHtml(text) {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = text; div.textContent = text;
@@ -595,9 +718,15 @@
} }
function logout() { function logout() {
if (blockchainCheckTimer) clearInterval(blockchainCheckTimer);
localStorage.removeItem('myaddress'); localStorage.removeItem('myaddress');
window.location.href = 'index.html'; window.location.href = 'index.html';
} }
// Cleanup on page unload
window.addEventListener('beforeunload', function() {
if (blockchainCheckTimer) clearInterval(blockchainCheckTimer);
});
</script> </script>
</body> </body>
</html> </html>
+460 -102
View File
@@ -7,169 +7,522 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"> <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"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style> <style>
body { * {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin: 0;
min-height: 100vh; padding: 0;
font-family: 'Segoe UI', sans-serif; box-sizing: border-box;
} }
body {
font-family: 'Segoe UI', sans-serif;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
position: relative;
}
/* Animated background elements */
.bg-animation {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
pointer-events: none;
}
.floating-shape {
position: absolute;
opacity: 0.1;
animation: float 6s ease-in-out infinite;
}
.floating-shape:nth-child(1) {
top: 10%;
left: 10%;
font-size: 60px;
animation-delay: 0s;
}
.floating-shape:nth-child(2) {
top: 20%;
right: 20%;
font-size: 80px;
animation-delay: 1s;
}
.floating-shape:nth-child(3) {
bottom: 30%;
left: 20%;
font-size: 70px;
animation-delay: 2s;
}
.floating-shape:nth-child(4) {
bottom: 20%;
right: 10%;
font-size: 50px;
animation-delay: 3s;
}
@keyframes float {
0%, 100% {
transform: translateY(0px) rotate(0deg);
}
50% {
transform: translateY(-20px) rotate(180deg);
}
}
.login-container { .login-container {
display: flex; display: flex;
align-items: center;
justify-content: center;
min-height: 100vh; min-height: 100vh;
padding: 20px; position: relative;
z-index: 1;
} }
.login-card { .left-panel {
background: white; flex: 1;
border-radius: 20px; display: flex;
padding: 40px; flex-direction: column;
box-shadow: 0 20px 40px rgba(0,0,0,0.1); justify-content: center;
max-width: 500px; align-items: center;
width: 100%; color: white;
} padding: 60px;
.app-header {
text-align: center; text-align: center;
margin-bottom: 40px; background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
min-height: 100vh;
} }
.app-logo { .app-logo {
font-size: 80px; font-size: 120px;
background: linear-gradient(135deg, #667eea, #764ba2); margin-bottom: 30px;
background: linear-gradient(45deg, #fff, #f0f8ff);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
margin-bottom: 20px; text-shadow: 0 0 50px rgba(255,255,255,0.3);
animation: pulse 3s ease-in-out infinite;
} }
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
.app-title { .app-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 20px;
text-shadow: 0 4px 20px rgba(0,0,0,0.3);
letter-spacing: 2px;
}
.app-subtitle {
font-size: 18px;
opacity: 0.9;
margin-bottom: 40px;
max-width: 500px;
line-height: 1.6;
}
.features-list {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 40px;
}
.feature-item {
display: flex;
align-items: center;
gap: 15px;
font-size: 16px;
opacity: 0.9;
}
.feature-item i {
font-size: 20px;
width: 30px;
}
.right-panel {
flex: 1;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
box-shadow: -10px 0 30px rgba(0,0,0,0.1);
min-height: 100vh;
}
.login-form {
width: 100%;
max-width: 500px;
}
.form-header {
text-align: center;
margin-bottom: 30px;
}
.form-title {
font-size: 32px; font-size: 32px;
color: #333; color: #333;
margin-bottom: 10px; margin-bottom: 15px;
font-weight: 700;
} }
.app-subtitle { .form-subtitle {
color: #666; color: #666;
font-size: 16px; font-size: 16px;
margin-bottom: 30px;
} }
.status-box { .status-box {
background: #f8f9fa; background: #f8f9fa;
border: 1px solid #e9ecef; border: 1px solid #e9ecef;
border-radius: 10px;
padding: 15px;
margin: 20px 0;
text-align: center;
}
.status-box.connected {
background: #d1ecf1;
border-color: #bee5eb;
color: #0c5460;
}
.status-box.error {
background: #f8d7da;
border-color: #f5c6cb;
color: #721c24;
}
.accounts-title {
font-size: 18px;
color: #333;
margin: 30px 0 15px 0;
font-weight: 600;
}
.account-card {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 15px; border-radius: 15px;
padding: 20px; padding: 20px;
margin: 10px 0; margin: 25px 0;
cursor: pointer; text-align: center;
font-size: 16px;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.status-box.connected {
background: linear-gradient(135deg, #d1ecf1, #bee5eb);
border-color: #17a2b8;
color: #0c5460;
animation: success-pulse 2s ease-in-out;
}
.status-box.error {
background: linear-gradient(135deg, #f8d7da, #f5c6cb);
border-color: #dc3545;
color: #721c24;
}
@keyframes success-pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
.accounts-section {
margin: 20px 0;
max-height: 400px;
overflow-y: auto;
}
.accounts-title {
font-size: 20px;
color: #333;
margin-bottom: 20px;
font-weight: 600;
text-align: center;
}
.account-card {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
border: 2px solid #dee2e6;
border-radius: 20px;
padding: 20px;
margin: 15px 0;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.account-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
transition: left 0.6s ease;
}
.account-card:hover::before {
left: 100%;
}
.account-card:hover { .account-card:hover {
border-color: #667eea; border-color: #667eea;
background: #e3f2fd; background: linear-gradient(135deg, #e3f2fd, #bbdefb);
transform: translateY(-2px); transform: translateY(-3px) scale(1.01);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.2);
} }
.account-card.selected { .account-card.selected {
background: linear-gradient(135deg, #667eea, #764ba2); background: linear-gradient(135deg, #667eea, #764ba2);
border-color: #667eea; border-color: #667eea;
color: white; color: white;
transform: translateY(-3px) scale(1.01);
box-shadow: 0 15px 30px rgba(102, 126, 234, 0.4);
} }
.account-name { .account-header {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 10px;
} }
.account-icon {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-size: 20px;
color: white;
transition: transform 0.3s ease;
}
.account-card:hover .account-icon {
transform: rotate(5deg);
}
.account-card.selected .account-icon {
background: rgba(255,255,255,0.2);
}
.account-name {
font-size: 18px;
font-weight: 600;
margin-bottom: 5px;
}
.account-balance {
font-size: 12px;
opacity: 0.8;
}
.account-address { .account-address {
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
font-size: 12px; font-size: 12px;
opacity: 0.8; opacity: 0.8;
word-break: break-all; word-break: break-all;
line-height: 1.4;
margin-top: 8px;
padding: 8px;
background: rgba(0,0,0,0.05);
border-radius: 8px;
} }
.account-card.selected .account-address {
background: rgba(255,255,255,0.1);
}
.login-btn { .login-btn {
background: linear-gradient(135deg, #667eea, #764ba2); background: linear-gradient(135deg, #667eea, #764ba2);
color: white; color: white;
border: none; border: none;
border-radius: 50px; border-radius: 50px;
padding: 15px 30px; padding: 18px 35px;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
width: 100%; width: 100%;
margin-top: 30px; margin-top: 30px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
text-transform: uppercase;
letter-spacing: 1px;
position: relative;
overflow: hidden;
} }
.login-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.6s ease;
}
.login-btn:hover::before {
left: 100%;
}
.login-btn:hover:not(:disabled) { .login-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #5a67d8, #6b46c1); background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-2px); transform: translateY(-3px);
box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4);
} }
.login-btn:disabled { .login-btn:disabled {
background: #ccc; background: #ccc;
cursor: not-allowed; cursor: not-allowed;
transform: none; transform: none;
box-shadow: none;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.login-container {
flex-direction: column;
}
.left-panel {
flex: 0 0 auto;
min-height: 50vh;
padding: 30px;
}
.app-logo {
font-size: 80px;
}
.app-title {
font-size: 32px;
}
.app-subtitle {
font-size: 14px;
}
.right-panel {
flex: 1;
min-height: auto;
padding: 30px;
}
.form-title {
font-size: 24px;
}
.accounts-section {
max-height: 300px;
}
}
.spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Scrollbar styling */
.accounts-section::-webkit-scrollbar {
width: 8px;
}
.accounts-section::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.accounts-section::-webkit-scrollbar-thumb {
background: #667eea;
border-radius: 10px;
}
.accounts-section::-webkit-scrollbar-thumb:hover {
background: #5a67d8;
} }
</style> </style>
</head> </head>
<body> <body>
<!-- Animated background -->
<div class="bg-animation">
<div class="floating-shape">
<i class="fas fa-comments"></i>
</div>
<div class="floating-shape">
<i class="fas fa-lock"></i>
</div>
<div class="floating-shape">
<i class="fas fa-shield-alt"></i>
</div>
<div class="floating-shape">
<i class="fas fa-globe"></i>
</div>
</div>
<div class="login-container"> <div class="login-container">
<div class="login-card"> <!-- Left Panel -->
<div class="app-header"> <div class="left-panel">
<div class="app-logo"> <div class="app-logo">
<i class="fas fa-comments"></i> <i class="fas fa-comments"></i>
</div>
<h1 class="app-title">BlockChat</h1>
<p class="app-subtitle">
Experience the future of secure messaging with blockchain technology.
Connect, communicate, and collaborate with complete privacy and transparency.
</p>
<div class="features-list">
<div class="feature-item">
<i class="fas fa-shield-alt"></i>
<span>End-to-end encrypted messaging</span>
</div>
<div class="feature-item">
<i class="fas fa-globe"></i>
<span>Decentralized blockchain network</span>
</div>
<div class="feature-item">
<i class="fas fa-infinity"></i>
<span>Permanent message history</span>
</div>
<div class="feature-item">
<i class="fas fa-bolt"></i>
<span>Lightning-fast message delivery</span>
</div> </div>
<h1 class="app-title">BlockChat</h1>
<p class="app-subtitle">Secure blockchain messaging</p>
</div> </div>
</div>
<div class="status-box error" id="statusBox">
<i class="fas fa-spinner fa-spin me-2"></i> <!-- Right Panel -->
<span>Connecting to blockchain...</span> <div class="right-panel">
<div class="login-form">
<div class="form-header">
<h2 class="form-title">Welcome Back</h2>
<p class="form-subtitle">Choose your blockchain account to continue</p>
</div>
<div class="status-box error" id="statusBox">
<div class="spinner"></div>
<span style="margin-left: 10px;">Connecting to blockchain network...</span>
</div>
<div class="accounts-section" id="accountsSection" style="display: none;">
<h3 class="accounts-title">Select Your Account</h3>
<div id="accountsList"></div>
</div>
<button class="login-btn" id="loginBtn" onclick="startChat()" disabled>
<i class="fas fa-rocket" style="margin-right: 10px;"></i>
Launch BlockChat
</button>
</div> </div>
<div id="accountsSection" style="display: none;">
<h3 class="accounts-title">Select Your Account</h3>
<div id="accountsList"></div>
</div>
<button class="login-btn" id="loginBtn" onclick="startChat()" disabled>
<i class="fas fa-rocket me-2"></i>
Enter BlockChat
</button>
</div> </div>
</div> </div>
@@ -207,14 +560,14 @@
} }
// Update status // Update status
updateStatus(true, `Connected! Found ${accounts.length} accounts`); updateStatus(true, `Connected successfully! Found ${accounts.length} accounts`);
// Show accounts // Show accounts
showAccounts(); showAccounts();
} catch (error) { } catch (error) {
console.error('❌ Connection failed:', error); console.error('❌ Connection failed:', error);
updateStatus(false, 'Connection failed - Make sure Anvil is running'); updateStatus(false, 'Connection failed - Make sure Anvil is running');
} }
} }
@@ -223,10 +576,10 @@
if (connected) { if (connected) {
statusBox.className = 'status-box connected'; statusBox.className = 'status-box connected';
statusBox.innerHTML = `<i class="fas fa-check-circle me-2"></i><span>${message}</span>`; statusBox.innerHTML = `<i class="fas fa-check-circle" style="margin-right: 10px;"></i><span>${message}</span>`;
} else { } else {
statusBox.className = 'status-box error'; statusBox.className = 'status-box error';
statusBox.innerHTML = `<i class="fas fa-exclamation-triangle me-2"></i><span>${message}</span>`; statusBox.innerHTML = `<i class="fas fa-exclamation-triangle" style="margin-right: 10px;"></i><span>${message}</span>`;
} }
} }
@@ -238,9 +591,14 @@
accounts.forEach((account, index) => { accounts.forEach((account, index) => {
html += ` html += `
<div class="account-card" onclick="selectAccount('${account}', ${index})"> <div class="account-card" onclick="selectAccount('${account}', ${index})">
<div class="account-name"> <div class="account-header">
<i class="fas fa-wallet me-2"></i> <div class="account-icon">
Account ${index + 1} <i class="fas fa-wallet"></i>
</div>
<div>
<div class="account-name">Account ${index + 1}</div>
<div class="account-balance">10,000 ETH Available</div>
</div>
</div> </div>
<div class="account-address">${account}</div> <div class="account-address">${account}</div>
</div> </div>