From 9c05c6a9d9aed053c77d9d9b6e9dfda720d8d022 Mon Sep 17 00:00:00 2001
From: stary <834207172@qq.com>
Date: Sun, 24 May 2026 12:43:16 +0800
Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E8=AF=9D=E6=A8=A1=E5=9D=97=E6=8B=86?=
=?UTF-8?q?=E5=88=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
client/public/connect-directory.js | 159 +++++++++++++++++++++++
client/public/connectview.js | 153 ++++------------------
client/public/invite-controller.js | 187 +++++++++++++++++++++++++++
client/public/main.js | 195 ++++-------------------------
4 files changed, 395 insertions(+), 299 deletions(-)
create mode 100644 client/public/connect-directory.js
create mode 100644 client/public/invite-controller.js
diff --git a/client/public/connect-directory.js b/client/public/connect-directory.js
new file mode 100644
index 0000000..5036797
--- /dev/null
+++ b/client/public/connect-directory.js
@@ -0,0 +1,159 @@
+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 = `
+
+
})
+
+
${escapeHtml(userName)}
+
${escapeHtml(identity)}
+
+
+
+ ${roleLabel}
+ ${isSelf ? `${SELF_LABEL}` : ''}
+
+ `;
+ roomList.appendChild(userItem);
+ });
+
+ section.appendChild(roomList);
+ usersContainer.appendChild(section);
+ });
+
+ onlineUsersList.classList.remove('hidden');
+}
diff --git a/client/public/connectview.js b/client/public/connectview.js
index d5d62e8..fa36d8d 100644
--- a/client/public/connectview.js
+++ b/client/public/connectview.js
@@ -5,6 +5,12 @@
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
@@ -66,13 +72,8 @@ export async function initWebSocket() {
*/
async function refreshOnlineUsers(silent = true) {
try {
- const response = await fetch('/signaling/users');
- if (!response.ok) {
- throw new Error('Failed to fetch online users');
- }
- const data = await response.json();
- cachedOnlineUsers = Array.isArray(data.users) ? data.users : [];
- displayOnlineUsers(cachedOnlineUsers);
+ cachedOnlineUsers = await fetchOnlineUsers();
+ updateOnlineUsersList(cachedOnlineUsers);
if (!silent) {
showNotification(`当前共有 ${cachedOnlineUsers.length} 个WebSocket用户在线`);
}
@@ -90,24 +91,10 @@ async function refreshOnlineUsers(silent = true) {
async function getAllConnectionIds() {
showNotification('正在获取连接ID和在线用户...');
try {
- 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();
- cachedOnlineUsers = Array.isArray(usersData.users) ? usersData.users : [];
- displayConnectionIds(connectionData.connectionIds || []);
- displayOnlineUsers(cachedOnlineUsers);
+ const { connectionIds, users } = await fetchConnectionDirectory();
+ cachedOnlineUsers = users;
+ updateConnectionIdList(connectionIds);
+ updateOnlineUsersList(cachedOnlineUsers);
} catch (error) {
console.error('Error fetching connection IDs:', error);
showNotification('获取连接信息失败', 'error');
@@ -118,49 +105,22 @@ async function getAllConnectionIds() {
* 显示连接ID列表
* @param {string[]} connectionIds - 连接ID数组
*/
-function displayConnectionIds(connectionIds) {
+function updateConnectionIdList(connectionIds) {
const idsContainer = document.getElementById('idsContainer');
const connectionIdsList = document.getElementById('connectionIdsList');
+ renderConnectionIds({
+ connectionIds,
+ idsContainer,
+ connectionIdsList,
+ onSelectConnectionId: selectConnectionId
+ });
+
if (idsContainer) {
- idsContainer.innerHTML = '';
-
- if (connectionIds.length === 0) {
- idsContainer.innerHTML = '暂无可用的连接ID
';
- } else {
- connectionIds.forEach(id => {
- const idElement = document.createElement('div');
- idElement.className = 'flex items-center justify-between p-2 bg-white/5 rounded-lg hover:bg-white/10 cursor-pointer transition-colors';
- idElement.innerHTML = `
- ${id}
-
- `;
- idsContainer.appendChild(idElement);
- });
- }
-
- if (connectionIdsList) {
- connectionIdsList.classList.remove('hidden');
- }
-
showNotification(`找到 ${connectionIds.length} 个连接ID`);
}
}
-/**
- * 转义HTML特殊字符
- * @param {string} value - 原始字符串
- * @returns {string} 安全字符串
- */
-function escapeHtml(value) {
- return String(value || '')
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''');
-}
-
function getCurrentUserId() {
try {
const settings = JSON.parse(localStorage.getItem('userSettings') || '{}');
@@ -175,77 +135,18 @@ function getCurrentUserId() {
* 显示全部在线WebSocket用户
* @param {Array} users - 在线用户列表
*/
-function displayOnlineUsers(users) {
+function updateOnlineUsersList(users) {
const onlineUsersList = document.getElementById('onlineUsersList');
const usersContainer = document.getElementById('usersContainer');
const onlineUsersSummary = document.getElementById('onlineUsersSummary');
- if (!onlineUsersList || !usersContainer || !onlineUsersSummary) {
- return;
- }
-
- onlineUsersSummary.textContent = `${users.length} 个WebSocket用户在线`;
-
- usersContainer.innerHTML = '';
-
- if (users.length === 0) {
- usersContainer.innerHTML = '暂无在线用户
';
- onlineUsersList.classList.remove('hidden');
- return;
- }
-
- const groupedUsers = users.reduce((groups, user) => {
- const groupName = user.connectionId ? `房间 ${user.connectionId}` : '大厅(未加入房间)';
- 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} 人
- `;
- section.appendChild(roomTitle);
-
- const roomList = document.createElement('div');
- roomList.className = 'space-y-2';
-
- roomUsers.forEach((user) => {
- const userName = user.name || user.userId || '匿名用户';
- const avatar = user.avatar || '/images/p2.png';
- const roleLabel = user.role === 'host' ? '房主' : (user.role === 'participant' ? '成员' : '大厅');
- const isSelf = Boolean(user.userId) && user.userId === getCurrentUserId();
- const userItem = document.createElement('div');
- userItem.className = 'flex items-center justify-between rounded-lg bg-black/20 px-3 py-2';
- userItem.innerHTML = `
-
-
})
-
-
${escapeHtml(userName)}
-
${escapeHtml(user.userId || user.socketId || user.participantId || '未设置ID')}
-
-
-
- ${roleLabel}
- ${isSelf ? '自己' : ''}
-
- `;
- roomList.appendChild(userItem);
- });
-
- section.appendChild(roomList);
- usersContainer.appendChild(section);
+ renderOnlineUsers({
+ users,
+ currentUserId: getCurrentUserId(),
+ onlineUsersList,
+ usersContainer,
+ onlineUsersSummary
});
-
- onlineUsersList.classList.remove('hidden');
}
/**
diff --git a/client/public/invite-controller.js b/client/public/invite-controller.js
new file mode 100644
index 0000000..3e8f4f5
--- /dev/null
+++ b/client/public/invite-controller.js
@@ -0,0 +1,187 @@
+const DEFAULT_CALLER_NAME = '\u9080\u8bf7\u65b9';
+const DEFAULT_CALLER_AVATAR = '/images/p2.png';
+const DEFAULT_APPLY_REASON = '\u672a\u586b\u5199';
+
+function readConnectionIdFromSearch(search) {
+ return new URLSearchParams(search).get('connectionId') || '';
+}
+
+function hideInviteDialog() {
+ const dialog = document.getElementById('callRequestDialog');
+ if (dialog) {
+ dialog.classList.add('hidden');
+ }
+}
+
+function normalizeInviteCaller(caller = {}) {
+ return {
+ connectionId: caller.connectionId || '',
+ inviterSocketId: caller.inviterSocketId || '',
+ inviterUserId: caller.inviterUserId || '',
+ name: caller.name || caller.inviterName || DEFAULT_CALLER_NAME,
+ avatar: caller.avatar || caller.inviterAvatar || DEFAULT_CALLER_AVATAR,
+ applyReason: caller.applyReason || caller.reason || DEFAULT_APPLY_REASON
+ };
+}
+
+function renderInviteDialog(caller) {
+ const dialog = document.getElementById('callRequestDialog');
+ if (!dialog) {
+ return false;
+ }
+
+ const callRequestName = document.getElementById('callRequestName');
+ const callRequestAvatar = document.getElementById('callRequestAvatar');
+ const callRequestText = document.getElementById('callRequestText');
+ const callRequestReason = document.getElementById('callRequestReason');
+
+ if (callRequestName) {
+ callRequestName.textContent = caller.name;
+ }
+ if (callRequestAvatar) {
+ callRequestAvatar.src = caller.avatar;
+ }
+ if (callRequestText) {
+ callRequestText.textContent = caller.connectionId
+ ? `\u6b63\u5728\u9080\u8bf7\u60a8\u52a0\u5165\u901a\u8bdd (${caller.connectionId})`
+ : '\u6b63\u5728\u8bf7\u6c42\u4e0e\u60a8\u8fdb\u884c\u89c6\u9891\u901a\u8bdd';
+ }
+ if (callRequestReason) {
+ callRequestReason.textContent = caller.applyReason;
+ }
+
+ dialog.classList.remove('hidden');
+ return true;
+}
+
+export function createInviteController({
+ store,
+ notify,
+ onAcceptConnection,
+ getCurrentView,
+ getConnectionId,
+ setConnectionId
+}) {
+ let pendingInvite = null;
+ let signalHandlersBound = false;
+
+ function showCallRequestDialog(caller = {}) {
+ const normalizedCaller = normalizeInviteCaller(caller);
+ pendingInvite = normalizedCaller;
+
+ if (normalizedCaller.connectionId) {
+ setConnectionId(normalizedCaller.connectionId);
+ }
+
+ return renderInviteDialog(normalizedCaller);
+ }
+
+ function getInvitePayloadFromUrl(search = window.location.search) {
+ const params = new URLSearchParams(search);
+ if (params.get('invite') !== '1') {
+ return null;
+ }
+
+ return normalizeInviteCaller({
+ name: params.get('callerName'),
+ avatar: params.get('callerAvatar'),
+ connectionId: params.get('connectionId')
+ });
+ }
+
+ function bindSignalHandlers() {
+ if (signalHandlersBound) {
+ return;
+ }
+
+ store.onSocketEvent('invite-call', (payload) => {
+ const caller = normalizeInviteCaller(payload);
+ showCallRequestDialog(caller);
+ notify(`${caller.name} \u9080\u8bf7\u4f60\u52a0\u5165\u901a\u8bdd`);
+ });
+
+ signalHandlersBound = true;
+ }
+
+ async function acceptInvite() {
+ hideInviteDialog();
+
+ const targetConnectionId =
+ (pendingInvite && pendingInvite.connectionId) ||
+ getConnectionId() ||
+ localStorage.getItem('connectionId') ||
+ readConnectionIdFromSearch(window.location.search);
+
+ if (!targetConnectionId) {
+ notify('\u7f3a\u5c11\u8fde\u63a5ID\uff0c\u65e0\u6cd5\u63a5\u53d7\u9080\u8bf7', 'error');
+ return;
+ }
+
+ setConnectionId(targetConnectionId);
+
+ if (pendingInvite) {
+ try {
+ store.sendInviteAccepted({
+ connectionId: targetConnectionId,
+ targetSocketId: pendingInvite.inviterSocketId,
+ targetUserId: pendingInvite.inviterUserId
+ });
+ } catch (error) {
+ console.error('Error accepting invite:', error);
+ notify('\u63a5\u53d7\u9080\u8bf7\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5', 'error');
+ return;
+ }
+ }
+
+ pendingInvite = null;
+ notify('\u5df2\u63a5\u53d7\u901a\u8bdd\u8bf7\u6c42');
+
+ if (getCurrentView() !== 'call') {
+ await onAcceptConnection(targetConnectionId);
+ }
+ }
+
+ function rejectInvite() {
+ hideInviteDialog();
+
+ if (pendingInvite) {
+ try {
+ store.sendInviteRejected({
+ connectionId: pendingInvite.connectionId,
+ targetSocketId: pendingInvite.inviterSocketId,
+ targetUserId: pendingInvite.inviterUserId
+ });
+ } catch (error) {
+ console.error('Error rejecting invite:', error);
+ }
+ }
+
+ pendingInvite = null;
+ notify('\u5df2\u62d2\u7edd\u901a\u8bdd\u8bf7\u6c42');
+ }
+
+ function bindDialogEvents() {
+ window.showCallRequest = showCallRequestDialog;
+ window.rejectCall = rejectInvite;
+ window.acceptCall = acceptInvite;
+
+ const rejectCallButton = document.getElementById('rejectCall');
+ const acceptCallButton = document.getElementById('acceptCall');
+
+ if (rejectCallButton && !rejectCallButton.dataset.bound) {
+ rejectCallButton.addEventListener('click', rejectInvite);
+ rejectCallButton.dataset.bound = 'true';
+ }
+ if (acceptCallButton && !acceptCallButton.dataset.bound) {
+ acceptCallButton.addEventListener('click', acceptInvite);
+ acceptCallButton.dataset.bound = 'true';
+ }
+ }
+
+ return {
+ bindDialogEvents,
+ bindSignalHandlers,
+ getInvitePayloadFromUrl,
+ showCallRequestDialog
+ };
+}
diff --git a/client/public/main.js b/client/public/main.js
index d5c8e76..fe146e8 100644
--- a/client/public/main.js
+++ b/client/public/main.js
@@ -12,176 +12,16 @@ import {
initWebSocket,
loadUserSettings
} from './connectview.js';
+import { createInviteController } from './invite-controller.js';
// 全局变量
let connectionId = "";
// 当前视图状态:'connect' 或 'call'(可用于未来扩展)
let currentView = 'connect';
-let pendingIncomingInvite = null;
-let inviteHandlersBound = false;
+function updateConnectionId(nextConnectionId) {
+ connectionId = nextConnectionId || '';
-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 || '';
- const applyReason = caller.applyReason || caller.reason || '未填写';
- 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 (document.getElementById('callRequestReason')) {
- document.getElementById('callRequestReason').textContent = applyReason;
- }
-
- 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',
- applyReason: payload.applyReason || payload.reason || ''
- };
- 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';
+ if (connectionId) {
+ localStorage.setItem('connectionId', connectionId);
}
}
@@ -222,13 +62,22 @@ async function switchToCallView(connectionId) {
}
}
+const inviteController = createInviteController({
+ store,
+ notify: showNotification,
+ onAcceptConnection: switchToCallView,
+ getCurrentView: () => currentView,
+ getConnectionId: () => connectionId,
+ setConnectionId: updateConnectionId
+});
+
/**
* 处理加入通话
* @param {string} connectionId - 连接ID
*/
async function handleJoinCall(connectionId) {
showNotification(`正在加入通话 (${connectionId})`);
- localStorage.setItem('connectionId', connectionId);
+ updateConnectionId(connectionId);
await switchToCallView(connectionId);
}
@@ -240,7 +89,7 @@ async function handleCreateCall() {
//const connectionId = 'conn_' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
const connectionId = randomMeetingId();
- localStorage.setItem('connectionId', connectionId);
+ updateConnectionId(connectionId);
await switchToCallView(connectionId);
}
@@ -363,7 +212,7 @@ function bindCallViewDomEvents() {
}
});
- bindInviteDialogEvents();
+ inviteController.bindDialogEvents();
}
// 页面加载完成后初始化(SPA入口)
@@ -381,23 +230,23 @@ window.addEventListener('DOMContentLoaded', async () => {
// 初始化WebSocket连接(在connect视图就建立WebSocket)
await initWebSocket();
- bindInviteSignalHandlers();
+ inviteController.bindSignalHandlers();
// 绑定connect视图事件(加入通话、创建通话等)
bindConnectViewEvents(handleJoinCall, handleCreateCall);
- bindInviteDialogEvents();
+ inviteController.bindDialogEvents();
// 检查是否有保存的连接ID,填入输入框
const savedConnectionId = localStorage.getItem('connectionId');
if (savedConnectionId) {
- connectionId = savedConnectionId;
+ updateConnectionId(savedConnectionId);
const connectionIdInput = document.getElementById('connectionIdInput');
if (connectionIdInput) connectionIdInput.value = savedConnectionId;
}
- const invitePayload = getInvitePayloadFromUrl();
+ const invitePayload = inviteController.getInvitePayloadFromUrl();
if (invitePayload) {
- window.showCallRequest(invitePayload);
+ inviteController.showCallRequestDialog(invitePayload);
}
console.log('SPA initialized, showing connect view');