/** * 主入口文件 * 初始化应用,连接各个模块 * SPA架构:connect视图和call视图在同一页面切换 */ import store from './store.js'; import UIRenderer from './renderer.js'; import { showNotification, randomMeetingId } from './utils.js'; import chatMessage from './chatmessage.js'; import { bindConnectViewEvents, initWebSocket, loadUserSettings } from './connectview.js'; // 全局变量 let connectionId = ""; // 当前视图状态:'connect' 或 'call'(可用于未来扩展) let currentView = 'connect'; let pendingIncomingInvite = null; let inviteHandlersBound = false; function getInvitePayloadFromUrl() { const params = new URLSearchParams(window.location.search); if (params.get('invite') !== '1') { return null; } return { name: params.get('callerName') || '邀请方', avatar: params.get('callerAvatar') || '/images/p2.png', connectionId: params.get('connectionId') || '' }; } function showCallRequestDialog(caller = {}) { const dialog = document.getElementById('callRequestDialog'); if (!dialog) { return; } const callerName = caller.name || '邀请方'; const callerAvatar = caller.avatar || '/images/p2.png'; const targetConnectionId = caller.connectionId || ''; pendingIncomingInvite = caller; if (document.getElementById('callRequestName')) { document.getElementById('callRequestName').textContent = callerName; } if (document.getElementById('callRequestAvatar')) { document.getElementById('callRequestAvatar').src = callerAvatar; } if (document.getElementById('callRequestText')) { document.getElementById('callRequestText').textContent = targetConnectionId ? `正在邀请您加入通话 (${targetConnectionId})` : '正在请求与您进行视频通话'; } if (targetConnectionId) { connectionId = targetConnectionId; localStorage.setItem('connectionId', targetConnectionId); } dialog.classList.remove('hidden'); } function getCurrentUserProfile() { try { const settings = JSON.parse(localStorage.getItem('userSettings') || '{}'); return { userId: settings.userId || settings.id || '', name: settings.name || '我', avatar: settings.avatar || '/images/p1.png' }; } catch (error) { console.error('Error parsing user settings:', error); return { userId: '', name: '我', avatar: '/images/p1.png' }; } } function bindInviteSignalHandlers() { if (inviteHandlersBound) { return; } store.onSocketEvent('invite-call', (payload) => { pendingIncomingInvite = { connectionId: payload.connectionId, inviterSocketId: payload.inviterSocketId, inviterUserId: payload.inviterUserId, name: payload.inviterName || '邀请方', avatar: payload.inviterAvatar || '/images/p2.png' }; showCallRequestDialog(pendingIncomingInvite); showNotification(`${pendingIncomingInvite.name} 邀请你加入通话`); }); inviteHandlersBound = true; } function bindInviteDialogEvents() { window.showCallRequest = function (caller) { showCallRequestDialog(caller); }; window.rejectCall = function () { const dialog = document.getElementById('callRequestDialog'); if (dialog) { dialog.classList.add('hidden'); } if (pendingIncomingInvite) { try { store.sendInviteRejected({ connectionId: pendingIncomingInvite.connectionId || '', targetSocketId: pendingIncomingInvite.inviterSocketId || '', targetUserId: pendingIncomingInvite.inviterUserId || '' }); } catch (error) { console.error('Error rejecting invite:', error); } } pendingIncomingInvite = null; showNotification('已拒绝通话请求'); }; window.acceptCall = async function () { const dialog = document.getElementById('callRequestDialog'); if (dialog) { dialog.classList.add('hidden'); } const targetConnectionId = (pendingIncomingInvite && pendingIncomingInvite.connectionId) || connectionId || localStorage.getItem('connectionId') || new URLSearchParams(window.location.search).get('connectionId'); if (!targetConnectionId) { showNotification('缺少连接ID,无法接受邀请', 'error'); return; } connectionId = targetConnectionId; localStorage.setItem('connectionId', targetConnectionId); if (pendingIncomingInvite) { try { store.sendInviteAccepted({ connectionId: targetConnectionId, targetSocketId: pendingIncomingInvite.inviterSocketId || '', targetUserId: pendingIncomingInvite.inviterUserId || '' }); } catch (error) { console.error('Error accepting invite:', error); showNotification('接受邀请失败,请稍后重试', 'error'); return; } } showNotification('已接受通话请求'); pendingIncomingInvite = null; if (currentView !== 'call') { await switchToCallView(targetConnectionId); } }; const rejectCall = document.getElementById('rejectCall'); const acceptCall = document.getElementById('acceptCall'); if (rejectCall && !rejectCall.dataset.bound) { rejectCall.addEventListener('click', window.rejectCall); rejectCall.dataset.bound = 'true'; } if (acceptCall && !acceptCall.dataset.bound) { acceptCall.addEventListener('click', window.acceptCall); acceptCall.dataset.bound = 'true'; } } /** * 切换到call视图(创建/加入通话后) * @param {string} connectionId - 连接ID */ async function switchToCallView(connectionId) { const connectView = document.getElementById('connectView'); const callView = document.getElementById('callView'); if (connectView) connectView.classList.add('hidden'); if (callView) callView.classList.remove('hidden'); currentView = 'call'; try { // 初始化渲染器 const renderer = new UIRenderer(store); // 加入通话 await store.joinCall(connectionId); // 设置WebRTC连接 await store.setUp(connectionId); renderer.renderHeaderTitle(); // 绑定DOM事件 bindCallViewDomEvents(); console.log('Video call app initialized successfully'); return true; } catch (error) { console.error('Error initializing app:', error); showNotification('初始化失败,请刷新页面重试', 'error'); return false; } } /** * 处理加入通话 * @param {string} connectionId - 连接ID */ async function handleJoinCall(connectionId) { showNotification(`正在加入通话 (${connectionId})`); localStorage.setItem('connectionId', connectionId); await switchToCallView(connectionId); } /** * 处理创建通话 */ async function handleCreateCall() { showNotification('正在创建通话...'); //const connectionId = 'conn_' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); const connectionId = randomMeetingId(); localStorage.setItem('connectionId', connectionId); await switchToCallView(connectionId); } /** * 绑定call视图DOM事件 */ function bindCallViewDomEvents() { // 切换侧边栏 window.toggleSidebar = function () { chatMessage.toggleSidebar(); }; // 切换麦克风 window.toggleMute = function () { const state = store.getState(); const currentState = state.session.localUser.mediaState.audio; store.updateLocalMedia('audio', !currentState); }; // 切换视频 window.toggleVideo = function () { const state = store.getState(); const currentState = state.session.localUser.mediaState.video; store.updateLocalMedia('video', !currentState); }; // 切换本地视频(用于悬停控制) window.toggleLocalVideo = function () { window.toggleVideo(); }; // 切换录屏 window.toggleRecording = function () { const state = store.getState(); const currentState = state.session.localUser.mediaState.recording || false; store.updateLocalMedia('recording', !currentState); // 显示录制状态通知 if (!currentState) { showNotification('开始录制'); } else { showNotification('停止录制'); } }; // 更多选项菜单切换 window.toggleMoreOptions = function () { const menu = document.getElementById('moreOptionsMenu'); if (menu) { menu.classList.toggle('hidden'); } }; // 切换视频分辨率 window.changeResolution = function (width, height) { store.changeResolution(width, height); // 关闭菜单 const menu = document.getElementById('moreOptionsMenu'); if (menu) { menu.classList.add('hidden'); } }; // 结束通话 window.endCall = function () { // 显示确认对话框 document.getElementById('endCallDialog').classList.remove('hidden'); }; // 取消结束通话 window.cancelEndCall = function () { document.getElementById('endCallDialog').classList.add('hidden'); }; // 确认结束通话 window.confirmEndCall = function () { document.getElementById('endCallDialog').classList.add('hidden'); store.endCall(); showNotification('通话已结束'); }; // 绑定消息相关事件 chatMessage.bindMessageEvents(); // 键盘快捷键 document.addEventListener('keydown', (event) => { // 空格键静音 if (event.code === 'Space' && !event.target.matches('input, textarea')) { event.preventDefault(); window.toggleMute(); } // Ctrl+V 切换视频 if (event.ctrlKey && event.key === 'v') { event.preventDefault(); window.toggleVideo(); } }); // 绑定对话框事件 const cancelEndCall = document.getElementById('cancelEndCall'); const confirmEndCall = document.getElementById('confirmEndCall'); if (cancelEndCall) cancelEndCall.addEventListener('click', window.cancelEndCall); if (confirmEndCall) confirmEndCall.addEventListener('click', window.confirmEndCall); // 更多选项按钮事件 const moreOptionsBtn = document.getElementById('moreOptionsBtn'); if (moreOptionsBtn) { moreOptionsBtn.addEventListener('click', window.toggleMoreOptions); } // 点击外部关闭更多选项菜单 document.addEventListener('click', function(event) { const moreOptionsMenu = document.getElementById('moreOptionsMenu'); const moreOptionsBtnEl = document.getElementById('moreOptionsBtn'); if (moreOptionsMenu && moreOptionsBtnEl && !moreOptionsMenu.contains(event.target) && !moreOptionsBtnEl.contains(event.target)) { moreOptionsMenu.classList.add('hidden'); } }); bindInviteDialogEvents(); } // 页面加载完成后初始化(SPA入口) window.addEventListener('DOMContentLoaded', async () => { try { // 显示connect视图,隐藏call视图 const connectView = document.getElementById('connectView'); const callView = document.getElementById('callView'); if (connectView) connectView.classList.remove('hidden'); if (callView) callView.classList.add('hidden'); currentView = 'connect'; // 加载用户设置 loadUserSettings(); // 初始化WebSocket连接(在connect视图就建立WebSocket) await initWebSocket(); bindInviteSignalHandlers(); // 绑定connect视图事件(加入通话、创建通话等) bindConnectViewEvents(handleJoinCall, handleCreateCall); bindInviteDialogEvents(); // 检查是否有保存的连接ID,填入输入框 const savedConnectionId = localStorage.getItem('connectionId'); if (savedConnectionId) { connectionId = savedConnectionId; const connectionIdInput = document.getElementById('connectionIdInput'); if (connectionIdInput) connectionIdInput.value = savedConnectionId; } const invitePayload = getInvitePayloadFromUrl(); if (invitePayload) { window.showCallRequest(invitePayload); } console.log('SPA initialized, showing connect view'); } catch (error) { console.error('Error initializing SPA:', error); showNotification('初始化失败,请刷新页面重试', 'error'); } }); // 导出全局变量 export { store };