diff --git a/app.js b/app.js index 203cf07..6b39416 100644 --- a/app.js +++ b/app.js @@ -6,6 +6,7 @@ import Localdrive from 'localdrive'; import fs from 'fs'; import Hyperdrive from 'hyperdrive'; import Corestore from 'corestore'; + const store = new Corestore('./storage'); const drive = new Hyperdrive(store); @@ -16,6 +17,7 @@ let userName = 'Anonymous'; let userAvatar = ''; let registeredUsers = JSON.parse(localStorage.getItem('registeredUsers')) || {}; let peerCount = 0; +let currentRoom = null; async function initialize() { swarm = new Hyperswarm(); @@ -28,8 +30,10 @@ async function initialize() { const registerForm = document.querySelector('#register-form'); const selectAvatarButton = document.querySelector('#select-avatar'); const createChatRoomButton = document.querySelector('#create-chat-room'); - const joinForm = document.querySelector('#join-form'); + const joinChatRoomButton = document.querySelector('#join-chat-room'); const messageForm = document.querySelector('#message-form'); + const toggleSetupBtn = document.querySelector('#toggle-setup-btn'); + const removeRoomBtn = document.querySelector('#remove-room-btn'); if (registerForm) { registerForm.addEventListener('submit', registerUser); @@ -42,35 +46,28 @@ async function initialize() { if (createChatRoomButton) { createChatRoomButton.addEventListener('click', createChatRoom); } - if (joinForm) { - joinForm.addEventListener('submit', joinChatRoom); + if (joinChatRoomButton) { + joinChatRoomButton.addEventListener('click', joinChatRoom); } if (messageForm) { messageForm.addEventListener('submit', sendMessage); } + if (toggleSetupBtn) { + toggleSetupBtn.addEventListener('click', toggleSetupView); + } + if (removeRoomBtn) { + removeRoomBtn.addEventListener('click', leaveRoom); + } - // const savedUser = localStorage.getItem('currentUser'); - - // if (savedUser) { - // const user = JSON.parse(savedUser); - // userName = user.username; - // userAvatar = user.avatar || ''; - // const setupDiv = document.querySelector('#setup'); - // if (setupDiv) { - // setupDiv.classList.remove('hidden'); - // } - // } else { - const registerDiv = document.querySelector('#register'); - if (registerDiv) { - registerDiv.classList.remove('hidden'); - } - // } + const registerDiv = document.querySelector('#register'); + if (registerDiv) { + registerDiv.classList.remove('hidden'); + } swarm.on('connection', async (connection, info) => { peerCount++; updatePeerCount(); - // Send the current user's icon to the new peer const iconBuffer = await drive.get(`/icons/${userName}.png`); if (iconBuffer) { const iconMessage = JSON.stringify({ @@ -84,7 +81,6 @@ async function initialize() { connection.on('data', async (data) => { const messageObj = JSON.parse(data.toString()); if (messageObj.type === 'icon') { - // Save icon to local directory const username = messageObj.username; const avatarBuffer = Buffer.from(messageObj.avatar, 'base64'); await drive.put(`/icons/${username}.png`, avatarBuffer); @@ -105,67 +101,34 @@ async function initialize() { }); swarm.on('close', () => { - console.log('Swarm closed.'); + console.log('Swarm closed'); }); } -function updatePeerCount() { - const peersCountElement = document.querySelector('#peers-count'); - if (peersCountElement) { - peersCountElement.textContent = peerCount; - } -} - -async function registerUser(e) { +function registerUser(e) { e.preventDefault(); + const regUsername = document.querySelector('#reg-username').value; - const regUsernameInput = document.querySelector('#reg-username'); - if (!regUsernameInput) { - alert('Username input element not found.'); + if (registeredUsers[regUsername]) { + alert('Username already taken. Please choose another.'); return; } - const regUsername = regUsernameInput.value.trim(); - - if (!regUsername) { - alert('Please enter a username.'); - return; - } - - const loadingDiv = document.querySelector('#loading'); - loadingDiv.classList.remove('hidden'); - - const newUser = { username: regUsername, avatar: '' }; - localStorage.setItem('currentUser', JSON.stringify(newUser)); - - const fileInput = document.querySelector('#avatar-file'); - if (fileInput && fileInput.files.length > 0) { - const file = fileInput.files[0]; + const avatarFile = document.querySelector('#avatar-file').files[0]; + if (avatarFile) { const reader = new FileReader(); - reader.onload = async () => { - const fileBuffer = Buffer.from(reader.result); - await drive.put(`/icons/${regUsername}.png`, fileBuffer); - userAvatar = `http://localhost:1337/icons/${regUsername}.png`; - // Save avatar URL to currentUser.avatar - newUser.avatar = userAvatar; - localStorage.setItem('currentUser', JSON.stringify(newUser)); - - // Broadcast the icon to all connected peers - const iconMessage = JSON.stringify({ - type: 'icon', - username: regUsername, - avatar: fileBuffer.toString('base64'), - }); - - const peers = [...swarm.connections]; - for (const peer of peers) { - peer.write(iconMessage); - } + reader.onload = (event) => { + const buffer = new Uint8Array(event.target.result); + drive.put(`/icons/${regUsername}.png`, buffer); + userAvatar = URL.createObjectURL(new Blob([buffer])); + registeredUsers[regUsername] = userAvatar; + localStorage.setItem('registeredUsers', JSON.stringify(registeredUsers)); + continueRegistration(regUsername); }; - reader.readAsArrayBuffer(file); + reader.readAsArrayBuffer(avatarFile); + } else { + continueRegistration(regUsername); } - - continueRegistration(regUsername); } async function continueRegistration(regUsername) { @@ -182,15 +145,14 @@ async function continueRegistration(regUsername) { document.querySelector('#register').classList.add('hidden'); loadingDiv.classList.add('hidden'); - document.querySelector('#join-chat-room').addEventListener('click', joinChatRoom); - const randomTopic = crypto.randomBytes(32); - document.querySelector('#chat-room-topic').innerText = b4a.toString(randomTopic, 'hex'); + document.querySelector('#chat-room-topic').innerText = truncateHash(b4a.toString(randomTopic, 'hex')); } async function createChatRoom() { - // Generate a new random topic (32 byte string) const topicBuffer = crypto.randomBytes(32); + const topic = b4a.toString(topicBuffer, 'hex'); + addRoomToList(topic); joinSwarm(topicBuffer); } @@ -198,21 +160,51 @@ async function joinChatRoom(e) { e.preventDefault(); const topicStr = document.querySelector('#join-chat-room-topic').value; const topicBuffer = b4a.from(topicStr, 'hex'); + addRoomToList(topicStr); joinSwarm(topicBuffer); } async function joinSwarm(topicBuffer) { + if (currentRoom) { + currentRoom.destroy(); + } + document.querySelector('#setup').classList.add('hidden'); document.querySelector('#loading').classList.remove('hidden'); - // Join the swarm with the topic. Setting both client/server to true means that this app can act as both. 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; + document.querySelector('#chat-room-topic').innerText = truncateHash(topic); document.querySelector('#loading').classList.add('hidden'); document.querySelector('#chat').classList.remove('hidden'); + + currentRoom = discovery; + clearMessages(); +} + +function addRoomToList(topic) { + const roomList = document.querySelector('#room-list'); + const roomItem = document.createElement('li'); + roomItem.textContent = truncateHash(topic); + roomItem.dataset.topic = topic; + roomItem.addEventListener('click', () => switchRoom(topic)); + roomList.appendChild(roomItem); +} + +function switchRoom(topic) { + const topicBuffer = b4a.from(topic, 'hex'); + joinSwarm(topicBuffer); +} + +function leaveRoom() { + if (currentRoom) { + currentRoom.destroy(); + currentRoom = null; + } + document.querySelector('#chat').classList.add('hidden'); + document.querySelector('#setup').classList.remove('hidden'); } function sendMessage(e) { @@ -222,7 +214,6 @@ function sendMessage(e) { onMessageAdded(userName, message, userAvatar); - // Send the message to all peers (that you are connected to) const messageObj = JSON.stringify({ type: 'message', name: userName, @@ -242,7 +233,6 @@ function scrollToBottom() { container.scrollTop = container.scrollHeight; } - function onMessageAdded(from, message, avatar) { const $div = document.createElement('div'); $div.classList.add('message'); @@ -261,7 +251,6 @@ function onMessageAdded(from, message, avatar) { const $text = document.createElement('div'); $text.classList.add('message-text'); - // Render Markdown content const md = window.markdownit(); const markdownContent = md.render(message); $text.innerHTML = markdownContent; @@ -274,13 +263,27 @@ function onMessageAdded(from, message, avatar) { scrollToBottom(); } +function truncateHash(hash) { + return `${hash.slice(0, 6)}...${hash.slice(-6)}`; +} + async function updateIcon(username, avatarBuffer) { - // Update the icon in the local HTML if necessary - // This can be adjusted as per your needs const userIcon = document.querySelector(`img[src*="${username}.png"]`); if (userIcon) { userIcon.src = URL.createObjectURL(new Blob([avatarBuffer])); } } +function clearMessages() { + const messagesContainer = document.querySelector('#messages'); + while (messagesContainer.firstChild) { + messagesContainer.removeChild(messagesContainer.firstChild); + } +} + +function toggleSetupView() { + const setupDiv = document.querySelector('#setup'); + setupDiv.classList.toggle('hidden'); +} + initialize(); diff --git a/index.html b/index.html index 03ffb03..85357b1 100644 --- a/index.html +++ b/index.html @@ -18,64 +18,69 @@
-
- - \ No newline at end of file + diff --git a/style.css b/style.css index 6a1bc72..31d0176 100644 --- a/style.css +++ b/style.css @@ -17,6 +17,77 @@ body { overflow-y: auto; } + /* body { + display: flex; + } */ + + #sidebar { + width: 200px; + background-color: #2f3136; + color: white; + height: 100vh; + padding: 10px; + box-sizing: border-box; + } + + #room-list { + list-style: none; + padding: 0; + margin-bottom: 20px; + } + + #room-list li { + padding: 10px; + cursor: pointer; + margin-bottom: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + #room-list li:hover { + background-color: #40444b; + } + + #content { + flex: 1; + padding: 10px; + } + + #sidebar button { + width: 100%; + padding: 10px; + margin-bottom: 10px; + cursor: pointer; + background-color: #7289da; + border: none; + color: white; + font-size: 14px; + border-radius: 5px; + } + + #sidebar button:hover { + background-color: #5b6eae; + } + + #remove-room-btn { + margin-top: 10px; + padding: 10px; + background-color: #f04747; + border: none; + color: white; + cursor: pointer; + border-radius: 5px; + } + + #remove-room-btn:hover { + background-color: #c03535; + } + + .hidden { + display: none; + } + /* Header styles */ header { display: flex;