Files
video_socket-server/client/public/call/signaling/connect-directory.js
2026-05-25 22:58:11 +08:00

173 lines
6.7 KiB
JavaScript

import { createTextElement, textValue } from '../../shared/dom.js';
const EMPTY_CONNECTION_IDS_HTML = '<p class="text-gray-500 text-sm">\u6682\u65e0\u53ef\u7528\u7684\u8fde\u63a5ID</p>';
const EMPTY_USERS_HTML = '<p class="text-gray-500 text-sm">\u6682\u65e0\u5728\u7ebf\u7528\u6237</p>';
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');
}