fixed the javascript for @rooms.js

This commit is contained in:
2025-08-31 22:57:44 +02:00
parent 55e322434f
commit 0c38422385

View File

@@ -3,23 +3,63 @@ let roomsRefreshInterval = null;
let currentSortBy = 'activity'; let currentSortBy = 'activity';
let currentMinUsers = 0; let currentMinUsers = 0;
let isSubscribedToRooms = false; let isSubscribedToRooms = false;
let isRoomsBrowserOpen = false;
let retryAttempts = 0;
const MAX_RETRY_ATTEMPTS = 3;
// Configuration
const ROOMS_CONFIG = {
refreshInterval: 30000, // 30 seconds
maxRetries: 3,
retryDelay: 2000, // 2 seconds
socketTimeout: 25000, // 25 seconds to wait for socket
fallbackToHttp: true,
autoRefresh: true
};
// Show the public rooms browser // Show the public rooms browser
function showPublicRoomsBrowser() { function showPublicRoomsBrowser() {
console.log('Opening public rooms browser');
const browserElement = document.getElementById('publicRoomsBrowser'); const browserElement = document.getElementById('publicRoomsBrowser');
if (browserElement) { if (!browserElement) {
browserElement.style.display = 'block'; console.error('Public rooms browser element not found');
return;
}
browserElement.style.display = 'flex';
isRoomsBrowserOpen = true;
retryAttempts = 0;
// Reset scroll position
const browserContent = document.getElementById('browserContent');
if (browserContent) {
browserContent.scrollTop = 0;
}
// Load rooms and subscribe to updates
loadPublicRoomsWebSocket(); loadPublicRoomsWebSocket();
subscribeToPublicRooms(); subscribeToPublicRooms();
// Set up auto-refresh if enabled
if (ROOMS_CONFIG.autoRefresh && !roomsRefreshInterval) {
roomsRefreshInterval = setInterval(() => {
if (isRoomsBrowserOpen) {
console.log('Auto-refreshing public rooms');
loadPublicRoomsWebSocket();
}
}, ROOMS_CONFIG.refreshInterval);
} }
} }
// Close the public rooms browser // Close the public rooms browser
function closePublicRoomsBrowser() { function closePublicRoomsBrowser() {
console.log('Closing public rooms browser');
const browserElement = document.getElementById('publicRoomsBrowser'); const browserElement = document.getElementById('publicRoomsBrowser');
if (browserElement) { if (browserElement) {
browserElement.style.display = 'none'; browserElement.style.display = 'none';
} }
isRoomsBrowserOpen = false;
unsubscribeFromPublicRooms(); unsubscribeFromPublicRooms();
// Clear auto-refresh // Clear auto-refresh
@@ -31,114 +71,232 @@ function closePublicRoomsBrowser() {
// Subscribe to live public rooms updates // Subscribe to live public rooms updates
function subscribeToPublicRooms() { function subscribeToPublicRooms() {
if (typeof socket !== 'undefined' && socket !== null && socket.connected && !isSubscribedToRooms) { if (isSocketAvailable() && !isSubscribedToRooms) {
try {
socket.emit('subscribe_public_rooms'); socket.emit('subscribe_public_rooms');
isSubscribedToRooms = true; isSubscribedToRooms = true;
console.log('Subscribed to public rooms updates'); console.log('Subscribed to public rooms updates');
} catch (error) {
console.error('Error subscribing to public rooms:', error);
}
} }
} }
// Unsubscribe from public rooms updates // Unsubscribe from public rooms updates
function unsubscribeFromPublicRooms() { function unsubscribeFromPublicRooms() {
if (typeof socket !== 'undefined' && socket !== null && socket.connected && isSubscribedToRooms) { if (isSocketAvailable() && isSubscribedToRooms) {
try {
socket.emit('unsubscribe_public_rooms'); socket.emit('unsubscribe_public_rooms');
isSubscribedToRooms = false; isSubscribedToRooms = false;
console.log('Unsubscribed from public rooms updates'); console.log('Unsubscribed from public rooms updates');
} catch (error) {
console.error('Error unsubscribing from public rooms:', error);
}
} }
} }
// Load public rooms via WebSocket // Check if socket is available and connected
function isSocketAvailable() {
return typeof socket !== 'undefined' &&
socket !== null &&
socket.connected;
}
// Load public rooms via WebSocket with fallback
function loadPublicRoomsWebSocket() { function loadPublicRoomsWebSocket() {
if (!isRoomsBrowserOpen) {
console.log('Rooms browser not open, skipping load');
return;
}
try { try {
showLoadingState(); showLoadingState();
updateFiltersFromUI();
const sortSelect = document.getElementById('roomsSortSelect'); if (isSocketAvailable()) {
const minUsersFilter = document.getElementById('minUsersFilter'); console.log('Loading rooms via WebSocket');
currentSortBy = sortSelect ? sortSelect.value : 'activity';
currentMinUsers = minUsersFilter ? (minUsersFilter.value || 0) : 0;
if (typeof socket !== 'undefined' && socket !== null && socket.connected) {
socket.emit('request_public_rooms', { socket.emit('request_public_rooms', {
sort: currentSortBy, sort: currentSortBy,
min_users: currentMinUsers, min_users: currentMinUsers,
limit: 50 limit: 50
}); });
// Set a timeout for WebSocket response
setTimeout(() => {
if (document.getElementById('roomsLoading')?.style.display !== 'none') {
console.warn('WebSocket timeout, falling back to HTTP');
loadPublicRoomsHTTP();
}
}, 5000);
} else { } else {
// Fallback to HTTP API if WebSocket not available console.log('WebSocket not available, using HTTP fallback');
loadPublicRoomsHTTP(); loadPublicRoomsHTTP();
} }
} catch (error) { } catch (error) {
console.error('Error requesting public rooms via WebSocket:', error); console.error('Error requesting public rooms via WebSocket:', error);
loadPublicRoomsHTTP(); // Fallback to HTTP loadPublicRoomsHTTP();
} }
} }
// Fallback HTTP method // Update filters from UI elements
async function loadPublicRoomsHTTP() { function updateFiltersFromUI() {
try {
showLoadingState();
const sortSelect = document.getElementById('roomsSortSelect'); const sortSelect = document.getElementById('roomsSortSelect');
const minUsersFilter = document.getElementById('minUsersFilter'); const minUsersFilter = document.getElementById('minUsersFilter');
const sortBy = sortSelect ? sortSelect.value : 'activity'; currentSortBy = sortSelect?.value || 'activity';
const minUsers = minUsersFilter ? (minUsersFilter.value || 0) : 0; currentMinUsers = parseInt(minUsersFilter?.value || '0') || 0;
const response = await fetch(`/api/rooms/public?sort=${sortBy}&min_users=${minUsers}&limit=50`); console.log(`Updated filters: sort=${currentSortBy}, minUsers=${currentMinUsers}`);
}
// Enhanced HTTP fallback with retry logic
async function loadPublicRoomsHTTP() {
if (!isRoomsBrowserOpen) {
console.log('Rooms browser not open, skipping HTTP load');
return;
}
try {
showLoadingState();
updateFiltersFromUI();
console.log(`Loading rooms via HTTP (attempt ${retryAttempts + 1}/${MAX_RETRY_ATTEMPTS})`);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
const response = await fetch(
`/api/rooms/public?sort=${encodeURIComponent(currentSortBy)}&min_users=${currentMinUsers}&limit=50`,
{ signal: controller.signal }
);
clearTimeout(timeoutId);
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP ${response.status}`); throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} }
const data = await response.json(); const data = await response.json();
publicRoomsData = data.rooms || [];
// Also load stats if (!data || !Array.isArray(data.rooms)) {
throw new Error('Invalid response format');
}
publicRoomsData = data.rooms;
console.log(`Loaded ${publicRoomsData.length} rooms via HTTP`);
// Load stats separately
await loadRoomStats();
displayRooms();
retryAttempts = 0; // Reset on success
} catch (error) {
console.error('Error loading public rooms via HTTP:', error);
if (error.name === 'AbortError') {
console.error('Request timed out');
}
retryAttempts++;
if (retryAttempts < MAX_RETRY_ATTEMPTS) {
console.log(`Retrying in ${ROOMS_CONFIG.retryDelay}ms...`);
setTimeout(() => {
if (isRoomsBrowserOpen) {
loadPublicRoomsHTTP();
}
}, ROOMS_CONFIG.retryDelay);
} else {
showErrorState();
retryAttempts = 0;
}
}
}
// Load room statistics
async function loadRoomStats() {
try { try {
const statsResponse = await fetch('/api/rooms/stats'); const statsResponse = await fetch('/api/rooms/stats');
if (statsResponse.ok) { if (statsResponse.ok) {
const stats = await statsResponse.json(); const stats = await statsResponse.json();
updateStatsDisplay(stats); updateStatsDisplay(stats);
} }
} catch (statsError) {
console.warn('Failed to load room stats:', statsError);
}
displayRooms();
} catch (error) { } catch (error) {
console.error('Error loading public rooms via HTTP:', error); console.warn('Failed to load room stats:', error);
showErrorState();
} }
} }
// Refresh public rooms // Refresh public rooms with user feedback
function refreshPublicRooms() { function refreshPublicRooms() {
loadPublicRoomsWebSocket(); console.log('Manual refresh requested');
retryAttempts = 0; // Reset retry counter on manual refresh
// Show brief loading indicator
const refreshBtn = document.getElementById('refreshPublicRoomsBtn');
const originalText = refreshBtn?.textContent;
if (refreshBtn) {
refreshBtn.textContent = 'Refreshing...';
refreshBtn.disabled = true;
} }
// Update stats display loadPublicRoomsWebSocket();
// Reset button after delay
setTimeout(() => {
if (refreshBtn) {
refreshBtn.textContent = originalText || 'Refresh';
refreshBtn.disabled = false;
}
}, 2000);
}
// Update stats display with enhanced formatting
function updateStatsDisplay(stats) { function updateStatsDisplay(stats) {
const statsElement = document.getElementById('roomsStats'); const statsElement = document.getElementById('roomsStats');
if (statsElement && stats) { if (!statsElement || !stats) return;
statsElement.textContent = `${stats.public_rooms || 0} public rooms • ${stats.total_users || 0} users online`;
} const publicRooms = stats.public_rooms || 0;
const totalUsers = stats.total_users || 0;
const lastUpdated = new Date().toLocaleTimeString();
statsElement.innerHTML = `
<span>${publicRooms} public room${publicRooms !== 1 ? 's' : ''}</span>
<span>•</span>
<span>${totalUsers} user${totalUsers !== 1 ? 's' : ''} online</span>
<span style="color: #666; font-size: 0.85em;">• Updated ${lastUpdated}</span>
`;
} }
// Show loading state // Enhanced loading state
function showLoadingState() { function showLoadingState() {
const loadingElement = document.getElementById('roomsLoading'); const loadingElement = document.getElementById('roomsLoading');
const listElement = document.getElementById('roomsList'); const listElement = document.getElementById('roomsList');
const emptyElement = document.getElementById('roomsEmpty'); const emptyElement = document.getElementById('roomsEmpty');
if (loadingElement) loadingElement.style.display = 'flex'; if (loadingElement) {
loadingElement.style.display = 'flex';
loadingElement.innerHTML = `
<div style="display: flex; align-items: center; gap: 1rem; color: #888;">
<div class="loading-spinner" style="
width: 20px;
height: 20px;
border: 2px solid #333;
border-top: 2px solid #00ff88;
border-radius: 50%;
animation: spin 1s linear infinite;
"></div>
<span>Loading public rooms...</span>
</div>
`;
}
if (listElement) listElement.style.display = 'none'; if (listElement) listElement.style.display = 'none';
if (emptyElement) emptyElement.style.display = 'none'; if (emptyElement) emptyElement.style.display = 'none';
} }
// Show error state // Enhanced error state with retry options
function showErrorState() { function showErrorState() {
const loadingElement = document.getElementById('roomsLoading'); const loadingElement = document.getElementById('roomsLoading');
const listElement = document.getElementById('roomsList'); const listElement = document.getElementById('roomsList');
@@ -146,18 +304,29 @@ function showErrorState() {
if (loadingElement) loadingElement.style.display = 'none'; if (loadingElement) loadingElement.style.display = 'none';
if (listElement) listElement.style.display = 'none'; if (listElement) listElement.style.display = 'none';
if (emptyElement) { if (emptyElement) {
emptyElement.style.display = 'flex'; emptyElement.style.display = 'flex';
emptyElement.innerHTML = ` emptyElement.innerHTML = `
<div style="text-align: center; max-width: 400px;">
<div style="font-size: 3rem; margin-bottom: 1rem;">⚠️</div> <div style="font-size: 3rem; margin-bottom: 1rem;">⚠️</div>
<h3 style="margin: 0 0 0.5rem 0; color: #ffffff;">Failed to Load Rooms</h3> <h3 style="margin: 0 0 0.5rem 0; color: #ffffff;">Failed to Load Rooms</h3>
<p style="margin: 0 0 1rem 0;">Unable to connect to the server. Please try again.</p> <p style="margin: 0 0 1.5rem 0; color: #888; line-height: 1.5;">
<button class="btn" onclick="refreshPublicRooms()">Retry</button> Unable to connect to the server. This could be due to network issues or server maintenance.
</p>
<div style="display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap;">
<button class="btn" onclick="refreshPublicRooms()">Try Again</button>
<button class="btn btn-secondary" onclick="loadPublicRoomsHTTP()">Force HTTP</button>
</div>
<p style="margin-top: 1rem; color: #666; font-size: 0.85em;">
Attempted ${retryAttempts}/${MAX_RETRY_ATTEMPTS} retries
</p>
</div>
`; `;
} }
} }
// Display rooms // Enhanced room display with better error handling
function displayRooms() { function displayRooms() {
const roomsList = document.getElementById('roomsList'); const roomsList = document.getElementById('roomsList');
const roomsLoading = document.getElementById('roomsLoading'); const roomsLoading = document.getElementById('roomsLoading');
@@ -165,8 +334,20 @@ function displayRooms() {
if (roomsLoading) roomsLoading.style.display = 'none'; if (roomsLoading) roomsLoading.style.display = 'none';
if (!publicRoomsData || publicRoomsData.length === 0) { if (!Array.isArray(publicRoomsData) || publicRoomsData.length === 0) {
if (roomsEmpty) roomsEmpty.style.display = 'flex'; if (roomsEmpty) {
roomsEmpty.style.display = 'flex';
roomsEmpty.innerHTML = `
<div style="text-align: center;">
<div style="font-size: 2.5rem; margin-bottom: 1rem;">🏠</div>
<h3 style="margin: 0 0 0.5rem 0; color: #ffffff;">No Public Rooms Found</h3>
<p style="margin: 0 0 1rem 0; color: #888;">
${currentMinUsers > 0 ? `Try reducing the minimum users filter (currently ${currentMinUsers})` : 'Check back later or create your own room'}
</p>
<button class="btn btn-secondary" onclick="refreshPublicRooms()">Refresh</button>
</div>
`;
}
if (roomsList) roomsList.style.display = 'none'; if (roomsList) roomsList.style.display = 'none';
return; return;
} }
@@ -174,56 +355,112 @@ function displayRooms() {
if (roomsEmpty) roomsEmpty.style.display = 'none'; if (roomsEmpty) roomsEmpty.style.display = 'none';
if (roomsList) { if (roomsList) {
roomsList.style.display = 'block'; roomsList.style.display = 'block';
roomsList.innerHTML = publicRoomsData.map(room => createRoomCard(room)).join('');
try {
const roomsHtml = publicRoomsData.map(room => createRoomCard(room)).join('');
roomsList.innerHTML = roomsHtml;
console.log(`Displayed ${publicRoomsData.length} rooms`);
} catch (error) {
console.error('Error rendering rooms:', error);
showErrorState();
}
} }
} }
// Create room card HTML // Enhanced room card with better data handling
function createRoomCard(room) { function createRoomCard(room) {
if (!room) return ''; if (!room || typeof room !== 'object') {
console.warn('Invalid room data:', room);
return '';
}
const activityClass = getActivityClass(room.minutes_since_activity || 0); const roomId = sanitizeText(room.room_id || 'unknown');
const timeAgo = formatTimeAgo(room.minutes_since_activity || 0); const title = sanitizeText(room.title || room.room_id || 'Unnamed Room');
const description = sanitizeText(room.description || 'No description available');
const userCount = Math.max(0, parseInt(room.user_count) || 0);
const messageCount = Math.max(0, parseInt(room.message_count) || 0);
const minutesAgo = Math.max(0, parseInt(room.minutes_since_activity) || 0);
const activityClass = getActivityClass(minutesAgo);
const timeAgo = formatTimeAgo(minutesAgo);
return ` return `
<div class="room-card" onclick="joinPublicRoom('${escapeHtml(room.room_id || '')}')"> <div class="room-card"
<div class="room-title"> onclick="joinPublicRoom('${escapeHtml(roomId)}')"
${escapeHtml(room.title || room.room_id || 'Unnamed Room')} style="
<span class="activity-indicator ${activityClass}"></span> cursor: pointer;
transition: all 0.2s ease;
border: 1px solid #333;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
background: #2a2a2a;
"
onmouseover="this.style.background='#333'; this.style.borderColor='#555';"
onmouseout="this.style.background='#2a2a2a'; this.style.borderColor='#333';">
<div class="room-title" style="
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
font-size: 1.1rem;
font-weight: bold;
color: #ffffff;
">
${title}
<span class="activity-indicator ${activityClass}" style="
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
"></span>
</div> </div>
<div class="room-description">
${escapeHtml(room.description || 'No description')} <div class="room-description" style="
</div> color: #b0b0b0;
<div class="room-stats"> margin-bottom: 1rem;
<div class="room-stat"> line-height: 1.4;
font-size: 0.9rem;
">${description}</div>
<div class="room-stats" style="
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
font-size: 0.85rem;
color: #888;
">
<div class="room-stat" style="display: flex; align-items: center; gap: 0.25rem;">
<span>👥</span> <span>👥</span>
<span>${room.user_count || 0} user${(room.user_count || 0) !== 1 ? 's' : ''}</span> <span>${userCount} user${userCount !== 1 ? 's' : ''}</span>
</div> </div>
<div class="room-stat"> <div class="room-stat" style="display: flex; align-items: center; gap: 0.25rem;">
<span>💬</span> <span>💬</span>
<span>${room.message_count || 0} message${(room.message_count || 0) !== 1 ? 's' : ''}</span> <span>${messageCount} message${messageCount !== 1 ? 's' : ''}</span>
</div> </div>
<div class="room-stat"> <div class="room-stat" style="display: flex; align-items: center; gap: 0.25rem;">
<span>🕐</span> <span>🕐</span>
<span>Active ${timeAgo}</span> <span>Active ${timeAgo}</span>
</div> </div>
<div class="room-stat"> <div class="room-stat" style="display: flex; align-items: center; gap: 0.25rem;">
<span>🆔</span> <span>🆔</span>
<span>${escapeHtml(room.room_id || 'Unknown')}</span> <span style="font-family: monospace; font-size: 0.8em;">${roomId}</span>
</div> </div>
</div> </div>
</div> </div>
`; `;
} }
// Get activity indicator class // Enhanced activity classification
function getActivityClass(minutesAgo) { function getActivityClass(minutesAgo) {
if (minutesAgo <= 5) return 'activity-active'; if (minutesAgo <= 5) return 'activity-active';
if (minutesAgo <= 30) return 'activity-recent'; if (minutesAgo <= 30) return 'activity-recent';
if (minutesAgo <= 120) return 'activity-moderate';
return 'activity-old'; return 'activity-old';
} }
// Format time ago // Enhanced time formatting
function formatTimeAgo(minutes) { function formatTimeAgo(minutes) {
if (minutes < 1) return 'just now'; if (minutes < 1) return 'just now';
if (minutes < 60) return `${Math.floor(minutes)}m ago`; if (minutes < 60) return `${Math.floor(minutes)}m ago`;
@@ -232,21 +469,37 @@ function formatTimeAgo(minutes) {
if (hours < 24) return `${hours}h ago`; if (hours < 24) return `${hours}h ago`;
const days = Math.floor(hours / 24); const days = Math.floor(hours / 24);
return `${days}d ago`; if (days < 7) return `${days}d ago`;
const weeks = Math.floor(days / 7);
return `${weeks}w ago`;
} }
// Escape HTML to prevent XSS // Enhanced text sanitization
function sanitizeText(text) {
if (typeof text !== 'string') return '';
return text.trim().substring(0, 200); // Limit length and trim
}
// Enhanced HTML escaping
function escapeHtml(text) { function escapeHtml(text) {
if (!text) return ''; if (!text) return '';
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = text; div.textContent = String(text);
return div.innerHTML; return div.innerHTML;
} }
// Join a public room - integrated with your existing system // Enhanced room joining with validation
function joinPublicRoom(roomId) { function joinPublicRoom(roomId) {
if (!roomId) { if (!roomId || typeof roomId !== 'string') {
console.error('No room ID provided'); console.error('Invalid room ID provided:', roomId);
return;
}
// Validate room ID format
const roomIdPattern = /^[a-zA-Z0-9\-_]{1,32}$/;
if (!roomIdPattern.test(roomId)) {
console.error('Invalid room ID format:', roomId);
return; return;
} }
@@ -255,52 +508,65 @@ function joinPublicRoom(roomId) {
// Close the browser // Close the browser
closePublicRoomsBrowser(); closePublicRoomsBrowser();
// Fill in the room input with the selected room // Fill in the room input
const roomInput = document.getElementById('roomInput'); const roomInput = document.getElementById('roomInput');
if (roomInput) { if (roomInput) {
roomInput.value = roomId; roomInput.value = roomId;
roomInput.dispatchEvent(new Event('input', { bubbles: true }));
} }
// Clear password field since these are public rooms // Clear password field for public rooms
const roomPasswordInput = document.getElementById('roomPasswordInput'); const roomPasswordInput = document.getElementById('roomPasswordInput');
if (roomPasswordInput) { if (roomPasswordInput) {
roomPasswordInput.value = ''; roomPasswordInput.value = '';
} }
// Trigger your existing join room functionality // Trigger join room functionality with delay to ensure UI updates
setTimeout(() => {
const joinButton = document.getElementById('joinRoomBtn'); const joinButton = document.getElementById('joinRoomBtn');
if (joinButton) { if (joinButton) {
joinButton.click(); joinButton.click();
} else {
console.error('Join button not found');
} }
}, 100);
} }
// WebSocket event handlers for real-time updates // Enhanced WebSocket handlers
function setupPublicRoomsWebSocketHandlers() { function setupPublicRoomsWebSocketHandlers() {
if (typeof socket === 'undefined' || socket === null) { if (!isSocketAvailable()) {
console.log('Socket not available, using HTTP fallback'); console.log('Socket not available, WebSocket handlers not attached');
return; return;
} }
// Handle public rooms data response // Handle public rooms data response
socket.on('public_rooms_data', function(data) { socket.on('public_rooms_data', function(data) {
console.log('Received public rooms data via WebSocket:', data); console.log('Received public rooms data via WebSocket:', data);
publicRoomsData = data && data.rooms ? data.rooms : [];
if (data && Array.isArray(data.rooms)) {
publicRoomsData = data.rooms;
} else {
console.warn('Invalid public rooms data format');
publicRoomsData = [];
}
if (data && data.stats) { if (data && data.stats) {
updateStatsDisplay(data.stats); updateStatsDisplay(data.stats);
} }
if (isRoomsBrowserOpen) {
displayRooms(); displayRooms();
}
}); });
// Handle live updates to public rooms // Handle live updates
socket.on('public_rooms_updated', function(data) { socket.on('public_rooms_updated', function(data) {
console.log('Received live public rooms update:', data); console.log('Received live public rooms update:', data);
// Only update if the browser is currently open and we're subscribed if (isRoomsBrowserOpen && isSubscribedToRooms) {
const browserElement = document.getElementById('publicRoomsBrowser'); if (data && Array.isArray(data.rooms)) {
if (browserElement && browserElement.style.display === 'block' && isSubscribedToRooms) { publicRoomsData = data.rooms;
publicRoomsData = data && data.rooms ? data.rooms : []; }
if (data && data.stats) { if (data && data.stats) {
updateStatsDisplay(data.stats); updateStatsDisplay(data.stats);
@@ -310,57 +576,89 @@ function setupPublicRoomsWebSocketHandlers() {
} }
}); });
// Handle WebSocket errors for public rooms // Handle WebSocket errors
socket.on('public_rooms_error', function(data) { socket.on('public_rooms_error', function(data) {
console.error('Public rooms WebSocket error:', data); console.error('Public rooms WebSocket error:', data);
if (isRoomsBrowserOpen) {
showErrorState(); showErrorState();
}
}); });
console.log('Public rooms WebSocket handlers attached'); // Handle connection events
socket.on('connect', function() {
console.log('Socket connected, resubscribing if browser is open');
if (isRoomsBrowserOpen && !isSubscribedToRooms) {
subscribeToPublicRooms();
}
});
socket.on('disconnect', function() {
console.log('Socket disconnected');
isSubscribedToRooms = false;
});
console.log('Enhanced public rooms WebSocket handlers attached');
} }
// Auto-setup WebSocket handlers when page loads // Enhanced socket initialization
function waitForSocketAndSetupHandlers(retryCount = 0) { function waitForSocketAndSetupHandlers(retryCount = 0) {
// Check if socket is properly initialized and connected if (isSocketAvailable()) {
if (typeof socket !== 'undefined' && socket !== null && typeof socket.connected !== 'undefined' && socket.connected) {
setupPublicRoomsWebSocketHandlers(); setupPublicRoomsWebSocketHandlers();
console.log('WebSocket handlers setup successfully'); console.log('WebSocket handlers setup successfully');
return; return;
} }
// If we've retried too many times, give up and use HTTP only
if (retryCount > 100) { // 100 * 250ms = 25 seconds max wait if (retryCount > 100) { // 100 * 250ms = 25 seconds max wait
console.warn('Socket failed to initialize after 25 seconds, falling back to HTTP-only mode'); console.warn('Socket failed to initialize after 25 seconds, using HTTP-only mode');
return; return;
} }
// Log current socket state for debugging
if (retryCount % 20 === 0) { // Log every 5 seconds if (retryCount % 20 === 0) { // Log every 5 seconds
console.log(`Waiting for socket... (attempt ${retryCount + 1})`); console.log(`Waiting for socket... (attempt ${retryCount + 1})`);
console.log('Socket type:', typeof socket);
console.log('Socket value:', socket);
console.log('Socket.io library loaded:', typeof io !== 'undefined');
} }
// Continue waiting
setTimeout(() => waitForSocketAndSetupHandlers(retryCount + 1), 250); setTimeout(() => waitForSocketAndSetupHandlers(retryCount + 1), 250);
} }
// Event listeners for controls // Enhanced event listeners with better error handling
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Add null checks for DOM elements console.log('Setting up public rooms browser event listeners');
const sortSelect = document.getElementById('roomsSortSelect');
const minUsersFilter = document.getElementById('minUsersFilter');
// Browse button (this was missing!)
const browseButton = document.getElementById('browsePublicRoomsBtn');
if (browseButton) {
browseButton.addEventListener('click', showPublicRoomsBrowser);
console.log('Browse public rooms button listener attached');
} else {
console.warn('Browse public rooms button not found');
}
// Close button
const closeButton = document.getElementById('closePublicRoomsBrowserBtn');
if (closeButton) {
closeButton.addEventListener('click', closePublicRoomsBrowser);
}
// Sort selector
const sortSelect = document.getElementById('roomsSortSelect');
if (sortSelect) { if (sortSelect) {
sortSelect.addEventListener('change', refreshPublicRooms); sortSelect.addEventListener('change', refreshPublicRooms);
} }
// Min users filter
const minUsersFilter = document.getElementById('minUsersFilter');
if (minUsersFilter) { if (minUsersFilter) {
minUsersFilter.addEventListener('change', refreshPublicRooms); minUsersFilter.addEventListener('change', refreshPublicRooms);
minUsersFilter.addEventListener('input', debounce(refreshPublicRooms, 1000));
} }
// Setup backdrop click handler // Refresh button
const refreshButton = document.getElementById('refreshPublicRoomsBtn');
if (refreshButton) {
refreshButton.addEventListener('click', refreshPublicRooms);
}
// Backdrop click handler
const browserElement = document.getElementById('publicRoomsBrowser'); const browserElement = document.getElementById('publicRoomsBrowser');
if (browserElement) { if (browserElement) {
browserElement.addEventListener('click', function(e) { browserElement.addEventListener('click', function(e) {
@@ -370,22 +668,70 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
// Start the WebSocket setup process // Keyboard shortcuts
waitForSocketAndSetupHandlers(); document.addEventListener('keydown', function(e) {
if (!isRoomsBrowserOpen) return;
// Auto-refresh every 30 seconds when browser is open if (e.key === 'Escape') {
setInterval(function() { e.preventDefault();
const browserElement = document.getElementById('publicRoomsBrowser'); closePublicRoomsBrowser();
if (browserElement && browserElement.style.display === 'block') { } else if (e.key === 'F5' || (e.ctrlKey && e.key === 'r')) {
e.preventDefault();
refreshPublicRooms(); refreshPublicRooms();
} }
}, 30000);
}); });
// Close on escape key // Start WebSocket setup
document.addEventListener('keydown', function(e) { waitForSocketAndSetupHandlers();
const browserElement = document.getElementById('publicRoomsBrowser');
if (e.key === 'Escape' && browserElement && browserElement.style.display === 'block') { console.log('Public rooms browser setup complete');
closePublicRoomsBrowser(); });
// Utility: Debounce function
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func.apply(this, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Cleanup on page unload
window.addEventListener('beforeunload', function() {
if (isSubscribedToRooms) {
unsubscribeFromPublicRooms();
}
if (roomsRefreshInterval) {
clearInterval(roomsRefreshInterval);
} }
}); });
// Add CSS for activity indicators if not already present
if (!document.getElementById('roomsActivityStyles')) {
const style = document.createElement('style');
style.id = 'roomsActivityStyles';
style.textContent = `
.activity-active {
background-color: #00ff88 !important;
box-shadow: 0 0 6px rgba(0, 255, 136, 0.6);
}
.activity-recent {
background-color: #ffaa00 !important;
}
.activity-moderate {
background-color: #888 !important;
}
.activity-old {
background-color: #444 !important;
}
.room-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
`;
document.head.appendChild(style);
}