import Hyperswarm from 'hyperswarm'; import crypto from 'hypercore-crypto'; import b4a from 'b4a'; import ServeDrive from 'serve-drive'; import Localdrive from 'localdrive'; const sharedDrive = new Localdrive('./shared-storage'); let swarm; let userName = 'Anonymous'; let userAvatar = ''; let peerCount = 0; async function initialize() { swarm = new Hyperswarm(); await sharedDrive.ready(); const servePort = 1337; const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => sharedDrive }); 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 joinForm = document.querySelector('#join-form'); const messageForm = document.querySelector('#message-form'); if (registerForm) { registerForm.addEventListener('submit', registerUser); } if (selectAvatarButton) { selectAvatarButton.addEventListener('click', () => { document.querySelector('#avatar-file').click(); }); } if (createChatRoomButton) { createChatRoomButton.addEventListener('click', createChatRoom); } if (joinForm) { joinForm.addEventListener('submit', joinChatRoom); } if (messageForm) { messageForm.addEventListener('submit', sendMessage); } swarm.on('connection', (connection, info) => { peerCount++; updatePeerCount(); connection.on('data', async (data) => { const messageObj = JSON.parse(data.toString()); if (messageObj.type === 'avatarRequest') { const avatarBuffer = await getAvatar(messageObj.username); if (avatarBuffer) { connection.write(avatarBuffer); } } else if (messageObj.type === 'fileUpdate') { await processFileUpdate(messageObj); } else { onMessageAdded(messageObj.name, messageObj.message, messageObj.avatar); } }); connection.on('close', () => { peerCount--; updatePeerCount(); }); }); swarm.on('error', (err) => { console.error('Swarm error:', err); }); swarm.on('close', () => { console.log('Swarm closed.'); }); 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 randomTopic = crypto.randomBytes(32); document.querySelector('#chat-room-topic').innerText = b4a.toString(randomTopic, 'hex'); } function updatePeerCount() { const peersCountElement = document.querySelector('#peers-count'); if (peersCountElement) { peersCountElement.textContent = peerCount; } } async function getAvatar(username) { try { const avatarPath = `./shared-storage/icons/${username}.png`; const avatarBuffer = await sharedDrive.get(avatarPath); return avatarBuffer; } catch (error) { console.error('Error getting avatar:', error); return null; } } async function registerUser(e) { e.preventDefault(); const regUsernameInput = document.querySelector('#reg-username'); if (!regUsernameInput) { alert('Username input element not found.'); 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 reader = new FileReader(); reader.onload = async () => { const fileBuffer = Buffer.from(reader.result); await sharedDrive.put(`/icons/${regUsername}-${file.name}`, fileBuffer); userAvatar = `http://localhost:1337/icons/${regUsername}-${file.name}`; localStorage.setItem('avatarURL', userAvatar); }; reader.readAsArrayBuffer(file); } continueRegistration(regUsername); } 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'); document.querySelector('#join-chat-room').addEventListener('click', joinChatRoom); const randomTopic = crypto.randomBytes(32); document.querySelector('#chat-room-topic').innerText = b4a.toString(randomTopic, 'hex'); } async function createChatRoom() { const topicBuffer = crypto.randomBytes(32); joinSwarm(topicBuffer); } async function joinChatRoom(e) { e.preventDefault(); const topicStr = document.querySelector('#join-chat-room-topic').value; const topicBuffer = b4a.from(topicStr, 'hex'); joinSwarm(topicBuffer); } async function joinSwarm(topicBuffer) { 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; document.querySelector('#loading').classList.add('hidden'); document.querySelector('#chat').classList.remove('hidden'); } function sendMessage(e) { e.preventDefault(); const message = document.querySelector('#message').value; document.querySelector('#message').value = ''; onMessageAdded(userName, message, userAvatar); 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); } } async function processFileUpdate(messageObj) { try { const filePath = messageObj.filePath; const fileBuffer = await downloadFileFromPeer(messageObj.peerId, filePath); await sharedDrive.put(filePath, fileBuffer); console.log(`File ${filePath} updated successfully.`); } catch (error) { console.error('Error processing file update:', error); } } async function downloadFileFromPeer(peerId, filePath) { // Implement logic to download file from peer using Hyperswarm or other P2P mechanisms } 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.textContent = message; $content.appendChild($header); $content.appendChild($text); $div.appendChild($content); document.querySelector('#messages').appendChild($div); } initialize();