const EMPTY_CONNECTION_IDS_HTML = '
\u6682\u65e0\u53ef\u7528\u7684\u8fde\u63a5ID
'; const EMPTY_USERS_HTML = '\u6682\u65e0\u5728\u7ebf\u7528\u6237
'; const HALL_LABEL = '\u5927\u5385\uff08\u672a\u52a0\u5165\u623f\u95f4\uff09'; const HOST_LABEL = '\u623f\u4e3b'; const PARTICIPANT_LABEL = '\u6210\u5458'; const UNKNOWN_USER_LABEL = '\u533f\u540d\u7528\u6237'; const UNSET_USER_ID_LABEL = '\u672a\u8bbe\u7f6eID'; const SELF_LABEL = '\u81ea\u5df1'; const SELECT_LABEL = '\u9009\u62e9'; const USER_COUNT_SUFFIX = '\u4eba'; const ONLINE_USERS_SUMMARY_SUFFIX = ' \u4e2aWebSocket\u7528\u6237\u5728\u7ebf'; function escapeHtml(value) { return String(value || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } export async function fetchOnlineUsers() { const response = await fetch('/signaling/users'); if (!response.ok) { throw new Error('Failed to fetch online users'); } const data = await response.json(); return Array.isArray(data.users) ? data.users : []; } export async function fetchConnectionDirectory() { const [connectionResponse, usersResponse] = await Promise.all([ fetch('/signaling/connection-ids'), fetch('/signaling/users') ]); if (!connectionResponse.ok) { throw new Error('Failed to fetch connection IDs'); } if (!usersResponse.ok) { throw new Error('Failed to fetch online users'); } const connectionData = await connectionResponse.json(); const usersData = await usersResponse.json(); return { connectionIds: connectionData.connectionIds || [], users: Array.isArray(usersData.users) ? usersData.users : [] }; } export function renderConnectionIds({ connectionIds, idsContainer, connectionIdsList, onSelectConnectionId }) { if (!idsContainer) { return; } idsContainer.innerHTML = ''; if (connectionIds.length === 0) { idsContainer.innerHTML = EMPTY_CONNECTION_IDS_HTML; } else { connectionIds.forEach((connectionId) => { const item = document.createElement('div'); item.className = 'flex items-center justify-between p-2 bg-white/5 rounded-lg hover:bg-white/10 cursor-pointer transition-colors'; const label = document.createElement('span'); label.className = 'text-sm'; label.textContent = connectionId; const button = document.createElement('button'); button.className = 'text-xs bg-indigo-600 hover:bg-indigo-700 px-2 py-1 rounded'; button.type = 'button'; button.textContent = SELECT_LABEL; button.addEventListener('click', () => onSelectConnectionId(connectionId)); item.appendChild(label); item.appendChild(button); idsContainer.appendChild(item); }); } if (connectionIdsList) { connectionIdsList.classList.remove('hidden'); } } export function renderOnlineUsers({ users, currentUserId, onlineUsersList, usersContainer, onlineUsersSummary }) { if (!onlineUsersList || !usersContainer || !onlineUsersSummary) { return; } onlineUsersSummary.textContent = `${users.length}${ONLINE_USERS_SUMMARY_SUFFIX}`; usersContainer.innerHTML = ''; if (users.length === 0) { usersContainer.innerHTML = EMPTY_USERS_HTML; onlineUsersList.classList.remove('hidden'); return; } const groupedUsers = users.reduce((groups, user) => { const groupName = user.connectionId ? `\u623f\u95f4 ${user.connectionId}` : HALL_LABEL; if (!groups[groupName]) { groups[groupName] = []; } groups[groupName].push(user); return groups; }, {}); Object.entries(groupedUsers).forEach(([groupName, roomUsers]) => { const section = document.createElement('div'); section.className = 'rounded-lg border border-white/10 bg-white/5 p-3'; const roomTitle = document.createElement('div'); roomTitle.className = 'flex items-center justify-between mb-2'; roomTitle.innerHTML = ` ${escapeHtml(groupName)} ${roomUsers.length} ${USER_COUNT_SUFFIX} `; section.appendChild(roomTitle); const roomList = document.createElement('div'); roomList.className = 'space-y-2'; roomUsers.forEach((user) => { const userName = user.name || user.userId || UNKNOWN_USER_LABEL; const avatar = user.avatar || '/images/p2.png'; const roleLabel = user.role === 'host' ? HOST_LABEL : (user.role === 'participant' ? PARTICIPANT_LABEL : HALL_LABEL); const isSelf = Boolean(user.userId) && user.userId === currentUserId; const identity = user.userId || user.socketId || user.participantId || UNSET_USER_ID_LABEL; const userItem = document.createElement('div'); userItem.className = 'flex items-center justify-between rounded-lg bg-black/20 px-3 py-2'; userItem.innerHTML = `