diff --git a/app.js b/app.js index db71833..d1a2bdd 100644 --- a/app.js +++ b/app.js @@ -16,7 +16,7 @@ await drive.ready(); let swarm; let registeredUsers = JSON.parse(localStorage.getItem('registeredUsers')) || {}; let peerCount = 0; -let currentRoom = null; +let activeRooms = []; const eventEmitter = new EventEmitter(); // Define servePort at the top level @@ -29,7 +29,10 @@ let config = { rooms: [] }; -// Function to get a random port between 1337 and 2223 +// Store messages for each room +let messagesStore = {}; + +// Function to get a random port between 49152 and 65535 function getRandomPort() { return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152; } @@ -73,7 +76,10 @@ async function initialize() { toggleSetupBtn.addEventListener('click', toggleSetupView); } if (removeRoomBtn) { - removeRoomBtn.addEventListener('click', leaveRoom); + removeRoomBtn.addEventListener('click', () => { + const topic = document.querySelector('#chat-room-topic').innerText; + leaveRoom(topic); + }); } if (attachFileButton) { attachFileButton.addEventListener('click', () => fileInput.click()); @@ -85,7 +91,7 @@ async function initialize() { const configExists = fs.existsSync("./config.json"); if (configExists) { config = JSON.parse(fs.readFileSync("./config.json", 'utf8')); - console.log("Read config from file:", config) + console.log("Read config from file:", config); // Update port in URLs config.userAvatar = updatePortInUrl(config.userAvatar); config.rooms.forEach(room => { @@ -96,6 +102,12 @@ async function initialize() { } renderRoomList(); // Render the room list with aliases + + // Connect to all rooms on startup + for (const room of config.rooms) { + const topicBuffer = b4a.from(room.topic, 'hex'); + await joinSwarm(topicBuffer); + } } const registerDiv = document.querySelector('#register'); @@ -105,7 +117,7 @@ async function initialize() { eventEmitter.on('onMessage', async (messageObj) => { console.log('Received message:', messageObj); // Debugging log - + if (messageObj.type === 'icon') { const username = messageObj.username; if (messageObj.avatar) { @@ -120,12 +132,12 @@ async function initialize() { const fileBuffer = b4a.from(messageObj.file, 'base64'); await drive.put(`/files/${messageObj.fileName}`, fileBuffer); const fileUrl = `http://localhost:${servePort}/files/${messageObj.fileName}`; - addFileMessage(messageObj.name, messageObj.fileName, updatePortInUrl(fileUrl), messageObj.fileType, updatePortInUrl(messageObj.avatar)); + addFileMessage(messageObj.name, messageObj.fileName, updatePortInUrl(fileUrl), messageObj.fileType, updatePortInUrl(messageObj.avatar), messageObj.topic); } else { console.error('Received file message with missing file data or fileName:', messageObj); } } else if (messageObj.type === 'message') { - onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar); + onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar, messageObj.topic); } else { console.error('Received unknown message type:', messageObj); } @@ -223,7 +235,7 @@ async function createChatRoom() { const topicBuffer = crypto.randomBytes(32); const topic = b4a.toString(topicBuffer, 'hex'); addRoomToList(topic); - joinSwarm(topicBuffer); + await joinSwarm(topicBuffer); } async function joinChatRoom(e) { @@ -231,27 +243,21 @@ async function joinChatRoom(e) { const topicStr = document.querySelector('#join-chat-room-topic').value; const topicBuffer = b4a.from(topicStr, 'hex'); addRoomToList(topicStr); - joinSwarm(topicBuffer); + await joinSwarm(topicBuffer); } async function joinSwarm(topicBuffer) { - if (currentRoom) { - currentRoom.destroy(); - } - - document.querySelector('#setup').classList.add('hidden'); - document.querySelector('#loading').classList.remove('hidden'); - - const discovery = swarm.join(topicBuffer, { client: true, server: true }); - await discovery.flushed(); - const topic = b4a.toString(topicBuffer, 'hex'); - document.querySelector('#chat-room-topic').innerText = topic; // Set full topic here - document.querySelector('#loading').classList.add('hidden'); - document.querySelector('#chat').classList.remove('hidden'); + if (!activeRooms.some(room => room.topic === topic)) { + const discovery = swarm.join(topicBuffer, { client: true, server: true }); + await discovery.flushed(); - currentRoom = discovery; - clearMessages(); + activeRooms.push({ topic, discovery }); + + console.log('Joined room:', topic); // Debugging log + + renderMessagesForRoom(topic); + } } function addRoomToList(topic) { @@ -260,9 +266,7 @@ function addRoomToList(topic) { roomItem.textContent = truncateHash(topic); roomItem.dataset.topic = topic; - // Add double-click event listener for editing room name roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); - roomItem.addEventListener('click', () => switchRoom(topic)); roomList.appendChild(roomItem); @@ -270,19 +274,6 @@ function addRoomToList(topic) { writeConfigToFile("./config.json"); } -function addRoomToListWithoutWritingToConfig(topic) { - const roomList = document.querySelector('#room-list'); - const roomItem = document.createElement('li'); - roomItem.textContent = truncateHash(topic); - roomItem.dataset.topic = topic; - - // Add double-click event listener for editing room name - roomItem.addEventListener('dblclick', () => enterEditMode(roomItem)); - - roomItem.addEventListener('click', () => switchRoom(topic)); - roomList.appendChild(roomItem); -} - function enterEditMode(roomItem) { const originalText = roomItem.textContent; const topic = roomItem.dataset.topic; @@ -328,26 +319,38 @@ function exitEditMode(roomItem, input, topic) { } function switchRoom(topic) { - const topicBuffer = b4a.from(topic, 'hex'); - joinSwarm(topicBuffer); + console.log('Switching to room:', topic); // Debugging log + document.querySelector('#chat-room-topic').innerText = topic; // Set full topic here + clearMessages(); + renderMessagesForRoom(topic); + + // Show chat UI elements + document.querySelector('#chat').classList.remove('hidden'); + document.querySelector('#setup').classList.add('hidden'); } -function leaveRoom() { - if (currentRoom) { - const topic = b4a.toString(currentRoom.topic, 'hex'); - const roomItem = document.querySelector(`li[data-topic="${topic}"]`); - if (roomItem) { - roomItem.remove(); - } - - config.rooms = config.rooms.filter(e => e.topic !== topic); - writeConfigToFile("./config.json"); - - currentRoom.destroy(); - currentRoom = null; +function leaveRoom(topic) { + const roomIndex = activeRooms.findIndex(room => room.topic === topic); + if (roomIndex !== -1) { + const room = activeRooms[roomIndex]; + room.discovery.destroy(); + activeRooms.splice(roomIndex, 1); + } + + const roomItem = document.querySelector(`li[data-topic="${topic}"]`); + if (roomItem) { + roomItem.remove(); + } + + config.rooms = config.rooms.filter(e => e.topic !== topic); + writeConfigToFile("./config.json"); + + if (activeRooms.length > 0) { + switchRoom(activeRooms[0].topic); + } else { + document.querySelector('#chat').classList.add('hidden'); + document.querySelector('#setup').classList.remove('hidden'); } - document.querySelector('#chat').classList.add('hidden'); - document.querySelector('#setup').classList.remove('hidden'); } function sendMessage(e) { @@ -355,7 +358,11 @@ function sendMessage(e) { const message = document.querySelector('#message').value; document.querySelector('#message').value = ''; - onMessageAdded(config.userName, message, config.userAvatar); + const topic = document.querySelector('#chat-room-topic').innerText; + + console.log('Sending message:', message); // Debugging log + + onMessageAdded(config.userName, message, config.userAvatar, topic); let peersPublicKeys = []; peersPublicKeys.push([...swarm.connections].map(peer => peer.remotePublicKey.toString('hex'))); @@ -367,7 +374,7 @@ function sendMessage(e) { name: config.userName, message, avatar: config.userAvatar, - topic: b4a.toString(currentRoom.topic, 'hex'), + topic: topic, peers: peersPublicKeys, // Deprecated. To be deleted in future updates timestamp: Date.now(), readableTimestamp: new Date().toLocaleString(), // Added human-readable timestamp @@ -389,6 +396,8 @@ async function handleFileInput(event) { await drive.put(filePath, buffer); const fileUrl = `http://localhost:${servePort}${filePath}`; + const topic = document.querySelector('#chat-room-topic').innerText; + const fileMessage = { type: 'file', name: config.userName, @@ -396,6 +405,7 @@ async function handleFileInput(event) { file: b4a.toString(buffer, 'base64'), fileType: file.type, avatar: updatePortInUrl(config.userAvatar), + topic: topic }; console.log('Sending file message:', fileMessage); // Debugging log @@ -405,7 +415,7 @@ async function handleFileInput(event) { peer.write(JSON.stringify(fileMessage)); } - addFileMessage(config.userName, file.name, fileUrl, file.type, config.userAvatar); + addFileMessage(config.userName, file.name, fileUrl, file.type, config.userAvatar, topic); }; reader.readAsArrayBuffer(file); } @@ -420,7 +430,7 @@ function sendFileMessage(name, fileUrl, fileType, avatar) { fileUrl, fileType, avatar, - topic: b4a.toString(currentRoom.topic, 'hex'), + topic: document.querySelector('#chat-room-topic').innerText, timestamp: Date.now(), }); @@ -429,10 +439,11 @@ function sendFileMessage(name, fileUrl, fileType, avatar) { peer.write(messageObj); } - addFileMessage(name, fileName, fileUrl, fileType, avatar); + addFileMessage(name, fileName, fileUrl, fileType, avatar, document.querySelector('#chat-room-topic').innerText); } -function addFileMessage(from, fileName, fileUrl, fileType, avatar) { +function addFileMessage(from, fileName, fileUrl, fileType, avatar, topic) { + console.log('Adding file message:', { from, fileName, fileUrl, fileType, avatar, topic }); // Debugging log const $div = document.createElement('div'); $div.classList.add('message'); @@ -459,17 +470,24 @@ function addFileMessage(from, fileName, fileUrl, fileType, avatar) { const $fileButton = document.createElement('button'); $fileButton.textContent = `Download File: ${fileName}`; $fileButton.onclick = function() { - const $fileLink = document.createElement('a'); - $fileLink.href = fileUrl; - $fileLink.download = fileName; - $fileLink.click(); + const $fileLink = document.createElement('a'); + $fileLink.href = fileUrl; + $fileLink.download = fileName; + $fileLink.click(); }; $content.appendChild($fileButton); } $div.appendChild($content); - document.querySelector('#messages').appendChild($div); - scrollToBottom(); + + // Only render the message if it's for the current room + const currentTopic = document.querySelector('#chat-room-topic').innerText; + if (currentTopic === topic) { + document.querySelector('#messages').appendChild($div); + scrollToBottom(); + } else { + console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic}`); // Debugging log + } } function updatePeerCount() { @@ -484,46 +502,61 @@ function scrollToBottom() { container.scrollTop = container.scrollHeight; } -function onMessageAdded(from, message, avatar) { - const $div = document.createElement('div'); - $div.classList.add('message'); - - const $img = document.createElement('img'); +function onMessageAdded(from, message, avatar, topic) { + console.log('Adding message:', { from, message, avatar, topic }); // Debugging log + const messageObj = { + from, + message, + avatar + }; - $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; // Default to a placeholder image if avatar URL is not provided - $img.classList.add('avatar'); - $div.appendChild($img); + // Add the message to the store + addMessageToStore(topic, messageObj); - const $content = document.createElement('div'); - $content.classList.add('message-content'); + // Only render messages for the current room + const currentTopic = document.querySelector('#chat-room-topic').innerText; + if (currentTopic === topic) { + const $div = document.createElement('div'); + $div.classList.add('message'); - const $header = document.createElement('div'); - $header.classList.add('message-header'); - $header.textContent = from; + const $img = document.createElement('img'); + $img.src = updatePortInUrl(avatar) || 'https://via.placeholder.com/40'; // Default to a placeholder image if avatar URL is not provided + $img.classList.add('avatar'); + $div.appendChild($img); - const $text = document.createElement('div'); - $text.classList.add('message-text'); + const $content = document.createElement('div'); + $content.classList.add('message-content'); - const md = window.markdownit({ - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return hljs.highlight(str, { language: lang }).value; - } catch (__) {} + const $header = document.createElement('div'); + $header.classList.add('message-header'); + $header.textContent = from; + + const $text = document.createElement('div'); + $text.classList.add('message-text'); + + const md = window.markdownit({ + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(str, { language: lang }).value; + } catch (__) {} + } + return ''; // use external default escaping } - return ''; // use external default escaping - } - }); + }); - const markdownContent = md.render(message); - $text.innerHTML = markdownContent; + const markdownContent = md.render(message); + $text.innerHTML = markdownContent; - $content.appendChild($header); - $content.appendChild($text); - $div.appendChild($content); + $content.appendChild($header); + $content.appendChild($text); + $div.appendChild($content); - document.querySelector('#messages').appendChild($div); - scrollToBottom(); + document.querySelector('#messages').appendChild($div); + scrollToBottom(); + } else { + console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic}`); // Debugging log + } } function truncateHash(hash) { @@ -583,6 +616,29 @@ function renderRoomList() { }); } +function renderMessagesForRoom(topic) { + console.log('Rendering messages for room:', topic); // Debugging log + // Clear the message area + clearMessages(); + + // Fetch and render messages for the selected room + const messages = getMessagesForRoom(topic); + messages.forEach(message => { + onMessageAdded(message.from, message.message, message.avatar, topic); + }); +} + +function getMessagesForRoom(topic) { + return messagesStore[topic] || []; +} + +function addMessageToStore(topic, messageObj) { + if (!messagesStore[topic]) { + messagesStore[topic] = []; + } + messagesStore[topic].push(messageObj); +} + // Call this function when loading the rooms initially renderRoomList();