lots of changes

This commit is contained in:
Radon 2025-08-19 19:45:02 -05:00
parent e7561bdc49
commit 83e429731a
5 changed files with 63 additions and 30 deletions

View File

@ -2,12 +2,10 @@
* Add an emoji picker next to the send button in chat
# MINOR ADJUSTMENTS
* Increase size of the text entry box for chat
* Adjust the positioning of voice controls, they get crowded rightwards
# FIX NEEDED
* Media embed posts in chat can cause the chat not to fully scroll to the bottom
* User modal settings are not saved when the modal is closed
* User modal mic volume not functioning to change microphone levels for the user
* Echo cancellation/noise suppression may not be working, check implementation

View File

@ -53,6 +53,14 @@ const state = new AppState();
// ==================== UTILITY FUNCTIONS ====================
class Utils {
static playSound(filepath, volume) {
const audio = new Audio(filepath);
audio.volume = volume !== undefined ? volume : 0.5; // Default volume if not specified
audio.play().catch(error => {
console.error('Error playing sound:', error);
});
}
static getWebSocketUrl() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
return `${protocol}//${window.location.host}/ws`;
@ -308,10 +316,6 @@ class AudioManager {
}
const average = sum / bufferLength;
if (Math.random() < 0.01) {
console.log('🎤 Audio level:', average, 'threshold:', CONFIG.SPEAKING_THRESHOLD, 'muted:', state.isMuted, 'in voice:', state.isInVoiceChat);
}
if (average > CONFIG.SPEAKING_THRESHOLD && !state.isMuted && state.isInVoiceChat) {
if (!isSpeaking) {
isSpeaking = true;
@ -405,7 +409,7 @@ class AudioManager {
audioElement.playsInline = true;
const userVolume = state.userVolumes.get(userId) || 100;
audioElement.volume = userVolume / 100;
audioElement.volume = (state.headphoneVolume / 100) * (userVolume / 100);
audioElement.muted = state.isDeafened || state.mutedUsers.has(userId);
AudioManager.setupAudioEventListeners(audioElement, userId);
@ -432,7 +436,7 @@ class AudioManager {
class WebSocketManager {
static initialize() {
const wsUrl = Utils.getWebSocketUrl();
console.log('🔥 Attempting to connect to WebSocket:', wsUrl);
console.log('Attempting to connect to WebSocket:', wsUrl);
state.ws = new WebSocket(wsUrl);
WebSocketManager.setupEventHandlers();
@ -441,28 +445,26 @@ class WebSocketManager {
static setupEventHandlers() {
state.ws.onopen = () => {
UIManager.updateConnectionStatus(true);
console.log('🔥 WebSocket connected successfully');
console.log('WebSocket connected successfully');
};
state.ws.onmessage = (event) => {
console.log('🔴 RAW WebSocket message received:', event.data);
try {
const message = JSON.parse(event.data);
console.log('🟢 PARSED WebSocket message:', message);
WebSocketManager.handleMessage(message);
} catch (error) {
console.error('Error parsing WebSocket message:', error, event.data);
console.error('Error parsing WebSocket message:', error, event.data);
}
};
state.ws.onclose = (event) => {
UIManager.updateConnectionStatus(false);
console.log('🔥 WebSocket disconnected. Code:', event.code, 'Reason:', event.reason);
console.log('WebSocket disconnected. Code:', event.code, 'Reason:', event.reason);
WebSocketManager.handleDisconnection();
};
state.ws.onerror = (error) => {
console.error('🔥 WebSocket error:', error);
console.error('WebSocket error:', error);
UIManager.updateConnectionStatus(false);
};
}
@ -474,28 +476,31 @@ class WebSocketManager {
UserManager.updateUsersList(message.data);
break;
case 'user_speaking':
console.log('User speaking update:', message.userId, message.data);
// console.log('🎤 User speaking update:', message.userId, message.data);
UserManager.updateSpeakingStatus(message.userId, message.data);
break;
case 'username_error':
console.log('Username error:', message.error);
console.log('Username error:', message.error);
VoiceChatManager.handleUsernameError(message.error);
break;
case 'chat_message':
console.log('Chat message received:', message);
console.log('💬 Chat message received:', message);
ChatManager.handleChatMessage(message);
break;
case 'webrtc_offer':
console.log('🔗 WebRTC offer:', message);
PeerConnectionManager.handleWebRTCOffer(message);
break;
case 'webrtc_answer':
console.log('🔗 WebRTC answer:', message);
PeerConnectionManager.handleWebRTCAnswer(message);
break;
case 'webrtc_ice':
console.log('🔗 WebRTC ICE candidate:', message);
PeerConnectionManager.handleWebRTCIce(message);
break;
default:
console.log('Unknown message type:', message.type);
console.log('Unknown message type:', message.type);
}
}
@ -891,7 +896,7 @@ class PeerConnectionManager {
// ==================== USER MANAGEMENT ====================
class UserManager {
static updateUsersList(users) {
console.log('🟡 updateUsersList called with:', users);
console.log('updateUsersList called with:', users);
const usersList = document.getElementById('users-list');
const currentUserIds = users ? users.map(u => u.id) : [];
@ -904,11 +909,21 @@ class UserManager {
UserManager.setupPeerConnections(users);
}
static onVoiceChatJoin() {
Utils.playSound('/sounds/join.wav', 0.25);
}
static onVoiceChatLeave() {
Utils.playSound('/sounds/leave.wav', 0.25);
}
static detectUserChanges(users, currentUserIds, previousUserIds) {
if (state.previousUsers && state.isInVoiceChat) {
const newUsers = users ? users.filter(u => !previousUserIds.includes(u.id) && u.username !== state.currentUsername) : [];
newUsers.forEach(user => {
ChatManager.showJoinAlert(user.username);
const joinSound = new Audio('/sounds/join.wav');
joinSound.play().catch(error => console.error('Failed to play join sound:', error));
});
const leftUserIds = previousUserIds.filter(id => !currentUserIds.includes(id));
@ -916,6 +931,9 @@ class UserManager {
const leftUser = state.previousUsers[userId];
if (leftUser && leftUser.username !== state.currentUsername) {
ChatManager.showLeaveAlert(leftUser.username);
// anonymouse function that gets asynced out
const leaveSound = new Audio('/sounds/leave.wav');
leaveSound.play().catch(error => console.error('Failed to play leave sound:', error));
}
});
}
@ -932,6 +950,7 @@ class UserManager {
static checkFirstJoin() {
if (state.currentUsername && !state.isInVoiceChat) {
UserManager.onVoiceChatJoin();
console.log('Username accepted, completing join process');
UIManager.showVoiceChatUI();
state.isInVoiceChat = true;
@ -1012,22 +1031,18 @@ class UserManager {
}
static updateSpeakingStatus(userId, isSpeaking) {
console.log('Updating speaking status for user:', userId, 'speaking:', isSpeaking);
const userCard = document.querySelector(`[data-user-id="${userId}"]`);
console.log('Found user card:', userCard);
if (userCard) {
if (isSpeaking) {
console.log('Adding speaking class to user card');
userCard.classList.add('speaking');
} else {
console.log('Removing speaking class from user card');
userCard.classList.remove('speaking');
}
} else {
console.error('User card not found for userId:', userId);
console.log('Error: User card not found for userId:', userId);
const allCards = document.querySelectorAll('.user-card');
console.log('All user cards:', Array.from(allCards).map(card => ({
console.log('Info: All user cards:', Array.from(allCards).map(card => ({
id: card.dataset.userId,
element: card
})));
@ -1088,7 +1103,7 @@ class ChatManager {
ChatManager.addChatAlert(`${username} left the chat`, 'leave');
}
static addChatAlert(message, type) {
static addChatAlert(message, type, timeout) {
const chatMessages = document.getElementById('chat-messages');
if (!chatMessages) return;
@ -1102,6 +1117,12 @@ class ChatManager {
<span class="alert-time">${Utils.formatTime(Date.now())}</span>
</div>
`;
if (timeout) {
alertEl.classList.add('timeout');
state.messageTimeouts.set(alertId, setTimeout(() => {
ChatManager.deleteMessage(alertId);
}, timeout));
}
chatMessages.appendChild(alertEl);
chatMessages.scrollTop = chatMessages.scrollHeight;
@ -1232,6 +1253,12 @@ class ModalManager {
modalUsername.textContent = 'Audio Settings';
const percentage = document.getElementById('headphone-volume-percentage');
if (percentage) {
console.log(percentage);
percentage.textContent = `${state.headphoneVolume}%`;
}
const modalBody = modal.querySelector('.modal-body');
modalBody.innerHTML = `
<div class="control-section">
@ -1247,7 +1274,7 @@ class ModalManager {
<label>Headphone Volume (All Incoming)</label>
<div class="volume-control">
<span>🔇</span>
<input type="range" id="headphone-volume-slider" min="0" max="100" value="100" oninput="VoiceControls.updateHeadphoneVolume()">
<input type="range" id="headphone-volume-slider" min="0" max="100" value=${state.headphoneVolume} oninput="VoiceControls.updateHeadphoneVolume()">
<span>🔊</span>
<span id="headphone-volume-percentage">100%</span>
</div>
@ -1266,6 +1293,13 @@ class ModalManager {
console.log('Opened self audio settings modal');
}
static closeSelfModal() {
const modal = document.getElementById('user-control-modal');
modal.style.display = 'none';
state.currentModalUserId = null;
console.log('Closed self audio settings modal');
}
static openUserModal(userId, username) {
state.currentModalUserId = userId;
const modal = document.getElementById('user-control-modal');
@ -1420,6 +1454,7 @@ class VoiceChatManager {
state.isInVoiceChat = false;
PeerConnectionManager.stopPeerHealthCheck();
PeerConnectionManager.cleanupAllConnections();
UserManager.onVoiceChatLeave();
document.getElementById('audio-container').innerHTML = '';
state.reset();

BIN
static/sounds/join.wav Normal file

Binary file not shown.

BIN
static/sounds/leave.wav Normal file

Binary file not shown.

View File

@ -441,9 +441,9 @@ main {
font-family: inherit;
transition: border-color 0.3s ease;
resize: none;
min-height: 50px;
max-height: 120px;
overflow-y: auto;
min-height: 60px;
max-height: 240px;
overflow-y: visible;
line-height: 1.4;
}