/** * connect视图逻辑 * 处理初始连接界面的UI、用户设置、WebSocket连接状态显示 */ import { showNotification } from './utils.js'; import store from './store.js'; import { fetchConnectionDirectory, fetchOnlineUsers, renderConnectionIds, renderOnlineUsers } from './connect-directory.js'; const MAX_AVATAR_SIZE = 2 * 1024 * 1024; // 2MB // WebSocket连接状态更新回调 let onWsStatusChange = null; let cachedOnlineUsers = []; /** * 设置WebSocket状态变化回调 * @param {function} callback - 回调函数(connected: boolean) */ export function setWsStatusCallback(callback) { onWsStatusChange = callback; } /** * 更新WebSocket状态显示 * @param {boolean} connected - 是否已连接 */ export function updateWsStatus(connected) { const wsStatusDot = document.getElementById('wsStatusDot'); const wsStatusText = document.getElementById('wsStatusText'); if (wsStatusDot && wsStatusText) { if (connected) { wsStatusDot.className = 'w-2 h-2 bg-green-500 rounded-full animate-pulse'; wsStatusText.textContent = 'WebSocket已连接'; } else { wsStatusDot.className = 'w-2 h-2 bg-gray-500 rounded-full'; wsStatusText.textContent = '未连接'; } } if (onWsStatusChange) { onWsStatusChange(connected); } } /** * 初始化WebSocket连接(页面加载时调用) */ export async function initWebSocket() { try { await store.connectSignaling(); store.syncSocketUserInfo(); updateWsStatus(true); await refreshOnlineUsers(); console.log('WebSocket initialized from connectview'); } catch (error) { console.error('Failed to initialize WebSocket:', error); updateWsStatus(false); showNotification('WebSocket连接失败,请刷新页面重试', 'error'); } } /** * 获取全部在线WebSocket用户 * @param {boolean} silent - 是否静默刷新 */ async function refreshOnlineUsers(silent = true) { try { cachedOnlineUsers = await fetchOnlineUsers(); updateOnlineUsersList(cachedOnlineUsers); if (!silent) { showNotification(`当前共有 ${cachedOnlineUsers.length} 个WebSocket用户在线`); } } catch (error) { console.error('Error fetching online users:', error); if (!silent) { showNotification('获取在线用户失败', 'error'); } } } /** * 获取所有连接ID */ async function getAllConnectionIds() { showNotification('正在获取连接ID和在线用户...'); try { const { connectionIds, users } = await fetchConnectionDirectory(); cachedOnlineUsers = users; updateConnectionIdList(connectionIds); updateOnlineUsersList(cachedOnlineUsers); } catch (error) { console.error('Error fetching connection IDs:', error); showNotification('获取连接信息失败', 'error'); } } /** * 显示连接ID列表 * @param {string[]} connectionIds - 连接ID数组 */ function updateConnectionIdList(connectionIds) { const idsContainer = document.getElementById('idsContainer'); const connectionIdsList = document.getElementById('connectionIdsList'); renderConnectionIds({ connectionIds, idsContainer, connectionIdsList, onSelectConnectionId: selectConnectionId }); if (idsContainer) { showNotification(`找到 ${connectionIds.length} 个连接ID`); } } function getCurrentUserId() { try { const settings = JSON.parse(localStorage.getItem('userSettings') || '{}'); return settings.userId || settings.id || ''; } catch (error) { console.error('Error parsing current user settings:', error); return ''; } } /** * 显示全部在线WebSocket用户 * @param {Array} users - 在线用户列表 */ function updateOnlineUsersList(users) { const onlineUsersList = document.getElementById('onlineUsersList'); const usersContainer = document.getElementById('usersContainer'); const onlineUsersSummary = document.getElementById('onlineUsersSummary'); renderOnlineUsers({ users, currentUserId: getCurrentUserId(), onlineUsersList, usersContainer, onlineUsersSummary }); } /** * 选择连接ID * @param {string} id - 连接ID */ function selectConnectionId(id) { const connectionIdInput = document.getElementById('connectionIdInput'); if (connectionIdInput) { connectionIdInput.value = id; showNotification(`已选择连接ID: ${id}`); } } /** * 生成8位用户ID * @returns {string} 用户ID */ function generateUserId() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = 'user_'; for (let i = 0; i < 8; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } /** * 加载用户设置 */ export function loadUserSettings() { const defaultAvatar = '/images/p1.png'; const userSettings = localStorage.getItem('userSettings'); if (userSettings) { try { const settings = JSON.parse(userSettings); if (settings.userId) { const userIdInput = document.getElementById('userIdInput'); if (userIdInput) userIdInput.value = settings.userId; } if (settings.name) { const nicknameInput = document.getElementById('nicknameInput'); const userName = document.getElementById('userName'); if (nicknameInput) nicknameInput.value = settings.name; if (userName) userName.textContent = settings.name; } const avatar = settings.avatar || defaultAvatar; const userAvatar = document.getElementById('userAvatar'); const avatarPreview = document.getElementById('avatarPreview'); if (userAvatar) userAvatar.src = avatar; if (avatarPreview) avatarPreview.src = avatar; } catch (error) { console.error('Error loading user settings:', error); const userAvatar = document.getElementById('userAvatar'); const avatarPreview = document.getElementById('avatarPreview'); if (userAvatar) userAvatar.src = defaultAvatar; if (avatarPreview) avatarPreview.src = defaultAvatar; } } else { const newUserId = generateUserId(); const userIdInput = document.getElementById('userIdInput'); if (userIdInput) userIdInput.value = newUserId; const userAvatar = document.getElementById('userAvatar'); const avatarPreview = document.getElementById('avatarPreview'); if (userAvatar) userAvatar.src = defaultAvatar; if (avatarPreview) avatarPreview.src = defaultAvatar; saveSettings(); } } /** * 保存用户设置 */ export function saveSettings() { const defaultAvatar = '/images/p1.png'; const nicknameInput = document.getElementById('nicknameInput'); const userIdInput = document.getElementById('userIdInput'); const avatarPreview = document.getElementById('avatarPreview'); const userName = document.getElementById('userName'); const userAvatar = document.getElementById('userAvatar'); const settings = { userId: userIdInput ? userIdInput.value : generateUserId(), name: nicknameInput ? (nicknameInput.value || '我') : '我', avatar: avatarPreview ? (avatarPreview.src || defaultAvatar) : defaultAvatar }; localStorage.setItem('userSettings', JSON.stringify(settings)); store.syncSocketUserInfo(settings); if (userName) userName.textContent = settings.name; if (userAvatar) userAvatar.src = settings.avatar; showNotification('设置已保存', 'success'); } /** * 处理头像上传 * @param {Event} event - 文件选择事件 */ export function handleAvatarUpload(event) { const file = event.target.files[0]; if (!file) return; if (!file.type.startsWith('image/')) { showNotification('请选择图片文件', 'error'); return; } if (file.size > MAX_AVATAR_SIZE) { showNotification('图片大小不能超过2MB', 'error'); return; } const formData = new FormData(); formData.append('avatar', file); const userIdInput = document.getElementById('userIdInput'); if (userIdInput) { formData.append('userId', userIdInput.value); } showNotification('正在上传头像...'); fetch('/api/upload/avatar', { method: 'POST', body: formData }) .then(response => { if (!response.ok) throw new Error('上传失败'); return response.json(); }) .then(data => { if (data.success && data.avatarUrl) { const avatarUrl = data.avatarUrl; const avatarPreview = document.getElementById('avatarPreview'); const userAvatar = document.getElementById('userAvatar'); if (avatarPreview) avatarPreview.src = avatarUrl; if (userAvatar) userAvatar.src = avatarUrl; saveSettings(); showNotification('头像上传成功', 'success'); } else { throw new Error('上传失败:' + (data.message || '未知错误')); } }) .catch(error => { console.error('Error uploading avatar:', error); showNotification('头像上传失败,请重试', 'error'); const defaultAvatar = '/images/p1.png'; const avatarPreview = document.getElementById('avatarPreview'); if (avatarPreview) avatarPreview.src = defaultAvatar; }); } /** * 复制用户ID到剪贴板 */ export function copyUserId() { const userIdInput = document.getElementById('userIdInput'); if (userIdInput) { userIdInput.select(); document.execCommand('copy'); showNotification('用户ID已复制到剪贴板', 'success'); } } /** * 切换设置菜单 */ export function toggleSettingsMenu() { const settingsMenu = document.getElementById('settingsMenu'); if (settingsMenu) { settingsMenu.classList.toggle('hidden'); } } /** * 绑定connect视图事件 * @param {function} onJoinCall - 加入通话回调(connectionId: string) * @param {function} onCreateCall - 创建通话回调() */ export function bindConnectViewEvents(onJoinCall, onCreateCall) { // 加入通话按钮 const connectBtn = document.getElementById('connectBtn'); if (connectBtn) { connectBtn.addEventListener('click', () => { const connectionIdInput = document.getElementById('connectionIdInput'); const connectionId = connectionIdInput ? connectionIdInput.value.trim() : ''; if (connectionId) { onJoinCall(connectionId); } else { showNotification('请输入连接ID', 'error'); } }); } // 创建通话按钮 const createCallBtn = document.getElementById('createCallBtn'); if (createCallBtn) { createCallBtn.addEventListener('click', onCreateCall); } // 浏览全部ID按钮 const browseIdsBtn = document.getElementById('browseIdsBtn'); if (browseIdsBtn) { browseIdsBtn.addEventListener('click', getAllConnectionIds); } // 输入框回车事件 const connectionIdInput = document.getElementById('connectionIdInput'); if (connectionIdInput) { connectionIdInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { const connectionId = connectionIdInput.value.trim(); if (connectionId) { onJoinCall(connectionId); } else { showNotification('请输入连接ID', 'error'); } } }); } // 用户设置按钮 const userSettingsBtn = document.getElementById('userSettingsBtn'); if (userSettingsBtn) { userSettingsBtn.addEventListener('click', toggleSettingsMenu); } } // 点击外部关闭设置菜单 document.addEventListener('click', function(event) { const settingsMenu = document.getElementById('settingsMenu'); const userSettingsBtn = document.getElementById('userSettingsBtn'); if (settingsMenu && userSettingsBtn && !settingsMenu.contains(event.target) && !userSettingsBtn.contains(event.target)) { settingsMenu.classList.add('hidden'); } }); // 导出全局函数(供HTML onclick使用) window.selectConnectionId = selectConnectionId; window.saveSettings = saveSettings; window.handleAvatarUpload = handleAvatarUpload; window.copyUserId = copyUserId; window.toggleSettingsMenu = toggleSettingsMenu;