mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-26 11:25:49 +00:00
Hackathon 14/sep/2025 Final
Signed-off-by: 5t4l1n <stalin78830@gmail.com>
This commit is contained in:
+226
-97
@@ -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
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user