import { createTextElement, textValue } from '../../shared/dom.js'; 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 getRoleTagClass(role) { if (role === 'host') { return 'text-xs px-2 py-1 rounded-full bg-indigo-500/20 text-indigo-300'; } if (role === 'participant') { return 'text-xs px-2 py-1 rounded-full bg-white/10 text-gray-300'; } return 'text-xs px-2 py-1 rounded-full bg-emerald-500/20 text-emerald-300'; } 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.appendChild(createTextElement('span', 'text-sm font-medium text-white', groupName)); roomTitle.appendChild(createTextElement('span', 'text-xs text-gray-400', `${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'; const profile = document.createElement('div'); profile.className = 'flex items-center gap-3 min-w-0'; const avatarImage = document.createElement('img'); avatarImage.src = textValue(avatar); avatarImage.alt = textValue(userName); avatarImage.className = 'w-8 h-8 rounded-full object-cover'; profile.appendChild(avatarImage); const info = document.createElement('div'); info.className = 'min-w-0'; info.appendChild(createTextElement('div', 'text-sm text-white truncate', userName)); info.appendChild(createTextElement('div', 'text-xs text-gray-400 truncate', identity)); profile.appendChild(info); const status = document.createElement('div'); status.className = 'flex items-center gap-2'; status.appendChild(createTextElement('span', getRoleTagClass(user.role), roleLabel)); if (isSelf) { status.appendChild(createTextElement('span', 'text-xs text-gray-500', SELF_LABEL)); } userItem.appendChild(profile); userItem.appendChild(status); roomList.appendChild(userItem); }); section.appendChild(roomList); usersContainer.appendChild(section); }); onlineUsersList.classList.remove('hidden'); }