Up to date fork #1

Merged
MiTask merged 33 commits from snxraven/LinkUp-P2P-Chat:main into main 2024-06-14 04:32:25 -04:00
Showing only changes of commit 3c803c811e - Show all commits

301
app.js
View File

@ -38,161 +38,165 @@ function getRandomPort() {
} }
async function initialize() { async function initialize() {
swarm = new Hyperswarm(); try {
swarm = new Hyperswarm();
servePort = getRandomPort(); servePort = getRandomPort();
const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive }); const serve = new ServeDrive({ port: servePort, get: ({ key, filename, version }) => drive });
await serve.ready(); await serve.ready();
console.log('Listening on http://localhost:' + serve.address().port); console.log('Listening on http://localhost:' + serve.address().port);
const registerForm = document.querySelector('#register-form'); const registerForm = document.querySelector('#register-form');
const selectAvatarButton = document.querySelector('#select-avatar'); const selectAvatarButton = document.querySelector('#select-avatar');
const createChatRoomButton = document.querySelector('#create-chat-room'); const createChatRoomButton = document.querySelector('#create-chat-room');
const joinChatRoomButton = document.querySelector('#join-chat-room'); const joinChatRoomButton = document.querySelector('#join-chat-room');
const messageForm = document.querySelector('#message-form'); const messageForm = document.querySelector('#message-form');
const toggleSetupBtn = document.querySelector('#toggle-setup-btn'); const toggleSetupBtn = document.querySelector('#toggle-setup-btn');
const removeRoomBtn = document.querySelector('#remove-room-btn'); const removeRoomBtn = document.querySelector('#remove-room-btn');
const attachFileButton = document.getElementById('attach-file'); const attachFileButton = document.getElementById('attach-file');
const fileInput = document.getElementById('file-input'); const fileInput = document.getElementById('file-input');
const talkButton = document.getElementById('talk-btn'); const talkButton = document.getElementById('talk-btn');
if (registerForm) { if (registerForm) {
registerForm.addEventListener('submit', registerUser); 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', () => {
const topic = document.querySelector('#chat-room-topic').innerText;
leaveRoom(topic);
});
}
if (attachFileButton) {
attachFileButton.addEventListener('click', () => fileInput.click());
}
if (fileInput) {
fileInput.addEventListener('change', handleFileInput);
}
if (talkButton) {
setupTalkButton();
}
const configExists = fs.existsSync("./config.json");
if (configExists) {
config = JSON.parse(fs.readFileSync("./config.json", 'utf8'));
console.log("Read config from file:", config);
// Update port in URLs
config.userAvatar = updatePortInUrl(config.userAvatar);
config.rooms.forEach(room => {
room.alias = room.alias || truncateHash(room.topic);
});
for (let user in config.registeredUsers) {
config.registeredUsers[user] = updatePortInUrl(config.registeredUsers[user]);
} }
if (selectAvatarButton) {
renderRoomList(); // Render the room list with aliases selectAvatarButton.addEventListener('click', () => {
document.querySelector('#avatar-file').click();
// 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');
if (registerDiv && !configExists) {
registerDiv.classList.remove('hidden');
}
eventEmitter.on('onMessage', async (messageObj) => {
console.log('Received message:', messageObj); // Debugging log
if (messageObj.type === 'icon') {
const username = messageObj.username;
if (messageObj.avatar) {
const avatarBuffer = b4a.from(messageObj.avatar, 'base64');
await drive.put(`/icons/${username}.png`, avatarBuffer);
updateIcon(username, avatarBuffer);
} else {
console.error('Received icon message with missing avatar data:', messageObj);
}
} else if (messageObj.type === 'file') {
if (messageObj.file && messageObj.fileName) {
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), 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, messageObj.topic);
} else if (messageObj.type === 'audio') {
const audioBuffer = b4a.from(messageObj.audio, 'base64');
const filePath = `/audio/${Date.now()}.webm`;
await drive.put(filePath, audioBuffer);
const audioUrl = `http://localhost:${servePort}${filePath}`;
addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic);
} else {
console.error('Received unknown message type:', messageObj);
}
});
swarm.on('connection', async (connection, info) => {
peerCount++;
updatePeerCount();
console.log('Peer connected, current peer count:', peerCount);
// Send the current user's icon to the new peer
const iconBuffer = await drive.get(`/icons/${config.userName}.png`);
if (iconBuffer) {
const iconMessage = JSON.stringify({
type: 'icon',
username: config.userName,
avatar: b4a.toString(iconBuffer, 'base64'),
}); });
connection.write(iconMessage); }
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', () => {
const topic = document.querySelector('#chat-room-topic').innerText;
leaveRoom(topic);
});
}
if (attachFileButton) {
attachFileButton.addEventListener('click', () => fileInput.click());
}
if (fileInput) {
fileInput.addEventListener('change', handleFileInput);
}
if (talkButton) {
setupTalkButton();
} }
connection.on('data', (data) => { const configExists = fs.existsSync("./config.json");
const messageObj = JSON.parse(data.toString()); if (configExists) {
eventEmitter.emit('onMessage', messageObj); config = JSON.parse(fs.readFileSync("./config.json", 'utf8'));
console.log("Read config from file:", config);
// Update port in URLs
config.userAvatar = updatePortInUrl(config.userAvatar);
config.rooms.forEach(room => {
room.alias = room.alias || truncateHash(room.topic);
});
for (let user in config.registeredUsers) {
config.registeredUsers[user] = updatePortInUrl(config.registeredUsers[user]);
}
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');
if (registerDiv && !configExists) {
registerDiv.classList.remove('hidden');
}
eventEmitter.on('onMessage', async (messageObj) => {
console.log('Received message:', messageObj); // Debugging log
if (messageObj.type === 'icon') {
const username = messageObj.username;
if (messageObj.avatar) {
const avatarBuffer = b4a.from(messageObj.avatar, 'base64');
await drive.put(`/icons/${username}.png`, avatarBuffer);
updateIcon(username, avatarBuffer);
} else {
console.error('Received icon message with missing avatar data:', messageObj);
}
} else if (messageObj.type === 'file') {
if (messageObj.file && messageObj.fileName) {
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), 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, messageObj.topic);
} else if (messageObj.type === 'audio') {
const audioBuffer = b4a.from(messageObj.audio, 'base64');
const filePath = `/audio/${Date.now()}.webm`;
await drive.put(filePath, audioBuffer);
const audioUrl = `http://localhost:${servePort}${filePath}`;
addAudioMessage(messageObj.name, audioUrl, messageObj.avatar, messageObj.topic);
} else {
console.error('Received unknown message type:', messageObj);
}
}); });
connection.on('close', () => { swarm.on('connection', async (connection, info) => {
peerCount--; peerCount++;
updatePeerCount(); updatePeerCount();
console.log('Peer disconnected, current peer count:', peerCount); console.log('Peer connected, current peer count:', peerCount);
// Send the current user's icon to the new peer
const iconBuffer = await drive.get(`/icons/${config.userName}.png`);
if (iconBuffer) {
const iconMessage = JSON.stringify({
type: 'icon',
username: config.userName,
avatar: b4a.toString(iconBuffer, 'base64'),
});
connection.write(iconMessage);
}
connection.on('data', (data) => {
const messageObj = JSON.parse(data.toString());
eventEmitter.emit('onMessage', messageObj);
});
connection.on('close', () => {
peerCount--;
updatePeerCount();
console.log('Peer disconnected, current peer count:', peerCount);
});
}); });
});
swarm.on('error', (err) => { swarm.on('error', (err) => {
console.error('Swarm error:', err); console.error('Swarm error:', err);
}); });
swarm.on('close', () => { swarm.on('close', () => {
console.log('Swarm closed'); console.log('Swarm closed');
}); });
// Initialize highlight.js once the DOM is fully loaded // Initialize highlight.js once the DOM is fully loaded
document.addEventListener("DOMContentLoaded", (event) => { document.addEventListener("DOMContentLoaded", (event) => {
hljs.highlightAll(); hljs.highlightAll();
}); });
} catch (error) {
console.error('Error during initialization:', error);
}
} }
function setupTalkButton() { function setupTalkButton() {
@ -321,14 +325,18 @@ async function joinChatRoom(e) {
async function joinSwarm(topicBuffer) { async function joinSwarm(topicBuffer) {
const topic = b4a.toString(topicBuffer, 'hex'); const topic = b4a.toString(topicBuffer, 'hex');
if (!activeRooms.some(room => room.topic === topic)) { if (!activeRooms.some(room => room.topic === topic)) {
const discovery = swarm.join(topicBuffer, { client: true, server: true }); try {
await discovery.flushed(); const discovery = swarm.join(topicBuffer, { client: true, server: true });
await discovery.flushed();
activeRooms.push({ topic, discovery }); activeRooms.push({ topic, discovery });
console.log('Joined room:', topic); // Debugging log console.log('Joined room:', topic); // Debugging log
renderMessagesForRoom(topic); renderMessagesForRoom(topic);
} catch (error) {
console.error('Error joining swarm for topic:', topic, error);
}
} }
} }
@ -667,6 +675,7 @@ function addAudioMessage(from, audioUrl, avatar, topic) {
console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic}`); // Debugging log console.log(`Message for topic ${topic} not rendered because current topic is ${currentTopic}`); // Debugging log
} }
} }
function truncateHash(hash) { function truncateHash(hash) {
return `${hash.slice(0, 6)}...${hash.slice(-6)}`; return `${hash.slice(0, 6)}...${hash.slice(-6)}`;
} }