From 310f1bd14dc0eabbd80181209a723b9f8f45715d Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sat, 8 Jun 2024 15:16:28 -0400 Subject: [PATCH] Update --- app.js | 552 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 276 insertions(+), 276 deletions(-) diff --git a/app.js b/app.js index 1e0fb79..20126ec 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events'; import Hyperswarm from 'hyperswarm'; import crypto from 'hypercore-crypto'; import b4a from 'b4a'; @@ -13,295 +12,296 @@ const drive = new Hyperdrive(store); await drive.ready(); -class ChatHandler extends EventEmitter { - constructor() { - super(); - this.userName = 'Anonymous'; - this.userAvatar = ''; - this.registeredUsers = JSON.parse(localStorage.getItem('registeredUsers')) || {}; - this.peerCount = 0; - this.currentRoom = null; - this.swarm = new Hyperswarm(); +let swarm; +let userName = 'Anonymous'; +let userAvatar = ''; +let registeredUsers = JSON.parse(localStorage.getItem('registeredUsers')) || {}; +let peerCount = 0; +let currentRoom = null; - this.initialize(); +async function initialize() { + swarm = new Hyperswarm(); + + const servePort = 1337; + const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive }); + await serve.ready(); + console.log('Listening on http://localhost:' + serve.address().port); + + const registerForm = document.querySelector('#register-form'); + const selectAvatarButton = document.querySelector('#select-avatar'); + const createChatRoomButton = document.querySelector('#create-chat-room'); + 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); + } + if (selectAvatarButton) { + selectAvatarButton.addEventListener('click', () => { + document.querySelector('#avatar-file').click(); + }); + } + if (createChatRoomButton) { + createChatRoomButton.addEventListener('click', createChatRoom); + } + if (joinChatRoomButton) { + joinChatRoomButton.addEventListener('click', joinChatRoom); + } + if (messageForm) { + messageForm.addEventListener('submit', sendMessage); + } + if (toggleSetupBtn) { + toggleSetupBtn.addEventListener('click', toggleSetupView); + } + if (removeRoomBtn) { + removeRoomBtn.addEventListener('click', leaveRoom); } - async initialize() { - const servePort = 1337; - const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive }); - await serve.ready(); - console.log('Listening on http://localhost:' + serve.address().port); + const registerDiv = document.querySelector('#register'); + if (registerDiv) { + registerDiv.classList.remove('hidden'); + } - const registerForm = document.querySelector('#register-form'); - const selectAvatarButton = document.querySelector('#select-avatar'); - const createChatRoomButton = document.querySelector('#create-chat-room'); - 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'); + swarm.on('connection', async (connection, info) => { + console.log('New connection established'); + peerCount++; + updatePeerCount(); - if (registerForm) { - registerForm.addEventListener('submit', (e) => this.registerUser(e)); - } - if (selectAvatarButton) { - selectAvatarButton.addEventListener('click', () => { - document.querySelector('#avatar-file').click(); + const iconBuffer = await drive.get(`/icons/${userName}.png`); + if (iconBuffer) { + const iconMessage = JSON.stringify({ + type: 'icon', + username: userName, + avatar: iconBuffer.toString('base64'), }); - } - if (createChatRoomButton) { - createChatRoomButton.addEventListener('click', () => this.createChatRoom()); - } - if (joinChatRoomButton) { - joinChatRoomButton.addEventListener('click', (e) => this.joinChatRoom(e)); - } - if (messageForm) { - messageForm.addEventListener('submit', (e) => this.sendMessage(e)); - } - if (toggleSetupBtn) { - toggleSetupBtn.addEventListener('click', () => this.toggleSetupView()); - } - if (removeRoomBtn) { - removeRoomBtn.addEventListener('click', () => this.leaveRoom()); - } - - const registerDiv = document.querySelector('#register'); - if (registerDiv) { - registerDiv.classList.remove('hidden'); - } - - this.swarm.on('connection', async (connection, info) => { - this.peerCount++; - this.updatePeerCount(); - - const iconBuffer = await drive.get(`/icons/${this.userName}.png`); - if (iconBuffer) { - const iconMessage = JSON.stringify({ - type: 'icon', - username: this.userName, - avatar: iconBuffer.toString('base64'), - }); - connection.write(iconMessage); - } - - connection.on('data', (data) => this.emit('onMessage', data, connection)); - connection.on('close', () => { - this.peerCount--; - this.updatePeerCount(); - }); - }); - - this.swarm.on('error', (err) => { - console.error('Swarm error:', err); - }); - - this.swarm.on('close', () => { - console.log('Swarm closed'); - }); - - this.on('onMessage', async (data, connection) => this.handleMessage(data, connection)); - } - - async registerUser(e) { - e.preventDefault(); - const regUsername = document.querySelector('#reg-username').value; - - if (this.registeredUsers[regUsername]) { - alert('Username already taken. Please choose another.'); - return; - } - - const avatarFile = document.querySelector('#avatar-file').files[0]; - if (avatarFile) { - const reader = new FileReader(); - reader.onload = (event) => { - const buffer = new Uint8Array(event.target.result); - drive.put(`/icons/${regUsername}.png`, buffer); - this.userAvatar = URL.createObjectURL(new Blob([buffer])); - this.registeredUsers[regUsername] = this.userAvatar; - localStorage.setItem('registeredUsers', JSON.stringify(this.registeredUsers)); - this.continueRegistration(regUsername); - }; - reader.readAsArrayBuffer(avatarFile); + console.log('Sending icon:', iconMessage); + connection.write(iconMessage); } else { - this.continueRegistration(regUsername); - } - } - - async continueRegistration(regUsername) { - const loadingDiv = document.querySelector('#loading'); - const setupDiv = document.querySelector('#setup'); - - if (!regUsername) { - alert('Please enter a username.'); - return; + console.log('Icon not found for user:', userName); } - this.userName = regUsername; - setupDiv.classList.remove('hidden'); - document.querySelector('#register').classList.add('hidden'); - loadingDiv.classList.add('hidden'); - - const randomTopic = crypto.randomBytes(32); - document.querySelector('#chat-room-topic').innerText = this.truncateHash(b4a.toString(randomTopic, 'hex')); - } - - async createChatRoom() { - const topicBuffer = crypto.randomBytes(32); - const topic = b4a.toString(topicBuffer, 'hex'); - this.addRoomToList(topic); - this.joinSwarm(topicBuffer); - } - - async joinChatRoom(e) { - e.preventDefault(); - const topicStr = document.querySelector('#join-chat-room-topic').value; - const topicBuffer = b4a.from(topicStr, 'hex'); - this.addRoomToList(topicStr); - this.joinSwarm(topicBuffer); - } - - async joinSwarm(topicBuffer) { - if (this.currentRoom) { - this.currentRoom.destroy(); - } - - document.querySelector('#setup').classList.add('hidden'); - document.querySelector('#loading').classList.remove('hidden'); - - const discovery = this.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'); - - this.currentRoom = discovery; - this.clearMessages(); - } - - addRoomToList(topic) { - const roomList = document.querySelector('#room-list'); - const roomItem = document.createElement('li'); - roomItem.textContent = this.truncateHash(topic); - roomItem.dataset.topic = topic; - roomItem.addEventListener('click', () => this.switchRoom(topic)); - roomList.appendChild(roomItem); - } - - switchRoom(topic) { - const topicBuffer = b4a.from(topic, 'hex'); - this.joinSwarm(topicBuffer); - } - - leaveRoom() { - if (this.currentRoom) { - const topic = b4a.toString(this.currentRoom.topic, 'hex'); - const roomItem = document.querySelector(`li[data-topic="${topic}"]`); - if (roomItem) { - roomItem.remove(); + connection.on('data', async (data) => { + const messageObj = JSON.parse(data.toString()); + console.log('Received message:', messageObj); + if (messageObj.type === 'icon') { + const username = messageObj.username; + const avatarBuffer = Buffer.from(messageObj.avatar, 'base64'); + await drive.put(`/icons/${username}.png`, avatarBuffer); + updateIcon(username, avatarBuffer); + } else { + onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar); } - this.currentRoom.destroy(); - this.currentRoom = null; - } - document.querySelector('#chat').classList.add('hidden'); - document.querySelector('#setup').classList.remove('hidden'); - } - - sendMessage(e) { - e.preventDefault(); - const message = document.querySelector('#message').value; - document.querySelector('#message').value = ''; - - this.onMessageAdded(this.userName, message, this.userAvatar); - - const messageObj = JSON.stringify({ - type: 'message', - name: this.userName, - message, - avatar: this.userAvatar, - timestamp: Date.now(), }); - const peers = [...this.swarm.connections]; - for (const peer of peers) { - peer.write(messageObj); - } + connection.on('close', () => { + console.log('Connection closed'); + peerCount--; + updatePeerCount(); + }); + }); + + swarm.on('error', (err) => { + console.error('Swarm error:', err); + }); + + swarm.on('close', () => { + console.log('Swarm closed'); + }); +} + +function registerUser(e) { + e.preventDefault(); + const regUsername = document.querySelector('#reg-username').value; + + if (registeredUsers[regUsername]) { + alert('Username already taken. Please choose another.'); + return; } - async handleMessage(data, connection) { - const messageObj = JSON.parse(data.toString()); - if (messageObj.type === 'icon') { - const username = messageObj.username; - const avatarBuffer = Buffer.from(messageObj.avatar, 'base64'); - await drive.put(`/icons/${username}.png`, avatarBuffer); - this.updateIcon(username, avatarBuffer); - } else { - this.onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar); - } - } - - scrollToBottom() { - var container = document.getElementById("messages-container"); - container.scrollTop = container.scrollHeight; - } - - onMessageAdded(from, message, avatar) { - const $div = document.createElement('div'); - $div.classList.add('message'); - - const $img = document.createElement('img'); - $img.src = avatar || 'https://via.placeholder.com/40'; - $div.appendChild($img); - - const $content = document.createElement('div'); - $content.classList.add('message-content'); - - 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(); - const markdownContent = md.render(message); - $text.innerHTML = markdownContent; - - $content.appendChild($header); - $content.appendChild($text); - $div.appendChild($content); - - document.querySelector('#messages').appendChild($div); - this.scrollToBottom(); - } - - truncateHash(hash) { - return `${hash.slice(0, 6)}...${hash.slice(-6)}`; - } - - async updateIcon(username, avatarBuffer) { - const userIcon = document.querySelector(`img[src*="${username}.png"]`); - if (userIcon) { - userIcon.src = URL.createObjectURL(new Blob([avatarBuffer])); - } - } - - clearMessages() { - const messagesContainer = document.querySelector('#messages'); - while (messagesContainer.firstChild) { - messagesContainer.removeChild(messagesContainer.firstChild); - } - } - - toggleSetupView() { - const setupDiv = document.querySelector('#setup'); - setupDiv.classList.toggle('hidden'); - } - - updatePeerCount() { - // Implement this method based on your needs to update the peer count in the UI + const avatarFile = document.querySelector('#avatar-file').files[0]; + if (avatarFile) { + const reader = new FileReader(); + 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(avatarFile); + } else { + continueRegistration(regUsername); } } -new ChatHandler(); +async function continueRegistration(regUsername) { + const loadingDiv = document.querySelector('#loading'); + const setupDiv = document.querySelector('#setup'); + + if (!regUsername) { + alert('Please enter a username.'); + return; + } + + userName = regUsername; + setupDiv.classList.remove('hidden'); + document.querySelector('#register').classList.add('hidden'); + loadingDiv.classList.add('hidden'); + + const randomTopic = crypto.randomBytes(32); + document.querySelector('#chat-room-topic').innerText = truncateHash(b4a.toString(randomTopic, 'hex')); +} + +async function createChatRoom() { + const topicBuffer = crypto.randomBytes(32); + const topic = b4a.toString(topicBuffer, 'hex'); + addRoomToList(topic); + joinSwarm(topicBuffer); +} + +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'); + + 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'); + + 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) { + const topic = b4a.toString(currentRoom.topic, 'hex'); + const roomItem = document.querySelector(`li[data-topic="${topic}"]`); + if (roomItem) { + roomItem.remove(); + } + currentRoom.destroy(); + currentRoom = null; + } + document.querySelector('#chat').classList.add('hidden'); + document.querySelector('#setup').classList.remove('hidden'); +} + +function sendMessage(e) { + e.preventDefault(); + const message = document.querySelector('#message').value; + document.querySelector('#message').value = ''; + + onMessageAdded(userName, message, userAvatar); + + const messageObj = JSON.stringify({ + type: 'message', + name: userName, + message, + avatar: userAvatar, + timestamp: Date.now(), + }); + + const peers = [...swarm.connections]; + for (const peer of peers) { + peer.write(messageObj); + } +} + +function scrollToBottom() { + var container = document.getElementById("messages-container"); + container.scrollTop = container.scrollHeight; +} + +function onMessageAdded(from, message, avatar) { + const $div = document.createElement('div'); + $div.classList.add('message'); + + const $img = document.createElement('img'); + $img.src = avatar || 'https://via.placeholder.com/40'; + $div.appendChild($img); + + const $content = document.createElement('div'); + $content.classList.add('message-content'); + + 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(); + const markdownContent = md.render(message); + $text.innerHTML = markdownContent; + + $content.appendChild($header); + $content.appendChild($text); + $div.appendChild($content); + + document.querySelector('#messages').appendChild($div); + scrollToBottom(); +} + +function truncateHash(hash) { + return `${hash.slice(0, 6)}...${hash.slice(-6)}`; +} + +async function updateIcon(username, avatarBuffer) { + const userIcon = document.querySelector(`img[src*="${username}.png"]`); + if (userIcon) { + userIcon.src = URL.createObjectURL(new Blob([avatarBuffer])); + } else { + const imgElements = document.querySelectorAll('img'); // Find all img elements + imgElements.forEach(img => { + if (img.src.endsWith(`${username}.png`)) { // Update if it matches the username + img.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();