Compare commits
3 Commits
66656c961c
...
52b5faf5a7
| Author | SHA1 | Date | |
|---|---|---|---|
| 52b5faf5a7 | |||
| 85c0b0226d | |||
| 2c6a7af31b |
@@ -161,6 +161,16 @@ function escapeHtml(value) {
|
|||||||
.replace(/'/g, ''');
|
.replace(/'/g, ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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用户
|
* 显示全部在线WebSocket用户
|
||||||
* @param {Array} users - 在线用户列表
|
* @param {Array} users - 在线用户列表
|
||||||
@@ -212,6 +222,7 @@ function displayOnlineUsers(users) {
|
|||||||
const userName = user.name || user.userId || '匿名用户';
|
const userName = user.name || user.userId || '匿名用户';
|
||||||
const avatar = user.avatar || '/images/p2.png';
|
const avatar = user.avatar || '/images/p2.png';
|
||||||
const roleLabel = user.role === 'host' ? '房主' : (user.role === 'participant' ? '成员' : '大厅');
|
const roleLabel = user.role === 'host' ? '房主' : (user.role === 'participant' ? '成员' : '大厅');
|
||||||
|
const isSelf = Boolean(user.userId) && user.userId === getCurrentUserId();
|
||||||
const userItem = document.createElement('div');
|
const userItem = document.createElement('div');
|
||||||
userItem.className = 'flex items-center justify-between rounded-lg bg-black/20 px-3 py-2';
|
userItem.className = 'flex items-center justify-between rounded-lg bg-black/20 px-3 py-2';
|
||||||
userItem.innerHTML = `
|
userItem.innerHTML = `
|
||||||
@@ -222,7 +233,10 @@ function displayOnlineUsers(users) {
|
|||||||
<div class="text-xs text-gray-400 truncate">${escapeHtml(user.userId || user.socketId || user.participantId || '未设置ID')}</div>
|
<div class="text-xs text-gray-400 truncate">${escapeHtml(user.userId || user.socketId || user.participantId || '未设置ID')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xs px-2 py-1 rounded-full ${user.role === 'host' ? 'bg-indigo-500/20 text-indigo-300' : (user.role === 'participant' ? 'bg-white/10 text-gray-300' : 'bg-emerald-500/20 text-emerald-300')}">${roleLabel}</span>
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="text-xs px-2 py-1 rounded-full ${user.role === 'host' ? 'bg-indigo-500/20 text-indigo-300' : (user.role === 'participant' ? 'bg-white/10 text-gray-300' : 'bg-emerald-500/20 text-emerald-300')}">${roleLabel}</span>
|
||||||
|
${isSelf ? '<span class="text-xs text-gray-500">自己</span>' : ''}
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
roomList.appendChild(userItem);
|
roomList.appendChild(userItem);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,169 @@ import {
|
|||||||
let connectionId = "";
|
let connectionId = "";
|
||||||
// 当前视图状态:'connect' 或 'call'(可用于未来扩展)
|
// 当前视图状态:'connect' 或 'call'(可用于未来扩展)
|
||||||
let currentView = 'connect';
|
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视图(创建/加入通话后)
|
* 切换到call视图(创建/加入通话后)
|
||||||
@@ -46,9 +209,11 @@ async function switchToCallView(connectionId) {
|
|||||||
bindCallViewDomEvents();
|
bindCallViewDomEvents();
|
||||||
|
|
||||||
console.log('Video call app initialized successfully');
|
console.log('Video call app initialized successfully');
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing app:', error);
|
console.error('Error initializing app:', error);
|
||||||
showNotification('初始化失败,请刷新页面重试', 'error');
|
showNotification('初始化失败,请刷新页面重试', 'error');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,14 +249,14 @@ function bindCallViewDomEvents() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 切换麦克风
|
// 切换麦克风
|
||||||
window.toggleMute = function (button) {
|
window.toggleMute = function () {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const currentState = state.session.localUser.mediaState.audio;
|
const currentState = state.session.localUser.mediaState.audio;
|
||||||
store.updateLocalMedia('audio', !currentState);
|
store.updateLocalMedia('audio', !currentState);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 切换视频
|
// 切换视频
|
||||||
window.toggleVideo = function (button) {
|
window.toggleVideo = function () {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const currentState = state.session.localUser.mediaState.video;
|
const currentState = state.session.localUser.mediaState.video;
|
||||||
store.updateLocalMedia('video', !currentState);
|
store.updateLocalMedia('video', !currentState);
|
||||||
@@ -103,7 +268,7 @@ function bindCallViewDomEvents() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 切换录屏
|
// 切换录屏
|
||||||
window.toggleRecording = function (button) {
|
window.toggleRecording = function () {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const currentState = state.session.localUser.mediaState.recording || false;
|
const currentState = state.session.localUser.mediaState.recording || false;
|
||||||
store.updateLocalMedia('recording', !currentState);
|
store.updateLocalMedia('recording', !currentState);
|
||||||
@@ -152,45 +317,6 @@ function bindCallViewDomEvents() {
|
|||||||
showNotification('通话已结束');
|
showNotification('通话已结束');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 显示通话请求弹窗
|
|
||||||
window.showCallRequest = function (caller) {
|
|
||||||
const dialog = document.getElementById('callRequestDialog');
|
|
||||||
if (dialog) {
|
|
||||||
// 设置通话请求信息
|
|
||||||
if (document.getElementById('callRequestName')) {
|
|
||||||
document.getElementById('callRequestName').textContent = caller.name;
|
|
||||||
}
|
|
||||||
if (document.getElementById('callRequestAvatar')) {
|
|
||||||
document.getElementById('callRequestAvatar').src = caller.avatar;
|
|
||||||
}
|
|
||||||
// 显示弹窗
|
|
||||||
dialog.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 拒绝通话
|
|
||||||
window.rejectCall = function () {
|
|
||||||
const dialog = document.getElementById('callRequestDialog');
|
|
||||||
if (dialog) {
|
|
||||||
dialog.classList.add('hidden');
|
|
||||||
}
|
|
||||||
showNotification('已拒绝通话请求');
|
|
||||||
// 可以在这里添加发送拒绝通话请求到服务器的逻辑
|
|
||||||
};
|
|
||||||
|
|
||||||
// 接受通话
|
|
||||||
window.acceptCall = function () {
|
|
||||||
const dialog = document.getElementById('callRequestDialog');
|
|
||||||
if (dialog) {
|
|
||||||
dialog.classList.add('hidden');
|
|
||||||
}
|
|
||||||
showNotification('已接受通话请求');
|
|
||||||
// 可以在这里添加发送接受通话请求到服务器的逻辑
|
|
||||||
// 然后初始化通话
|
|
||||||
store.initCall();
|
|
||||||
store.setUp(connectionId);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 绑定消息相关事件
|
// 绑定消息相关事件
|
||||||
chatMessage.bindMessageEvents();
|
chatMessage.bindMessageEvents();
|
||||||
|
|
||||||
@@ -232,11 +358,7 @@ function bindCallViewDomEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 绑定通话请求对话框事件
|
bindInviteDialogEvents();
|
||||||
const rejectCall = document.getElementById('rejectCall');
|
|
||||||
const acceptCall = document.getElementById('acceptCall');
|
|
||||||
if (rejectCall) rejectCall.addEventListener('click', window.rejectCall);
|
|
||||||
if (acceptCall) acceptCall.addEventListener('click', window.acceptCall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载完成后初始化(SPA入口)
|
// 页面加载完成后初始化(SPA入口)
|
||||||
@@ -254,17 +376,25 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
// 初始化WebSocket连接(在connect视图就建立WebSocket)
|
// 初始化WebSocket连接(在connect视图就建立WebSocket)
|
||||||
await initWebSocket();
|
await initWebSocket();
|
||||||
|
bindInviteSignalHandlers();
|
||||||
|
|
||||||
// 绑定connect视图事件(加入通话、创建通话等)
|
// 绑定connect视图事件(加入通话、创建通话等)
|
||||||
bindConnectViewEvents(handleJoinCall, handleCreateCall);
|
bindConnectViewEvents(handleJoinCall, handleCreateCall);
|
||||||
|
bindInviteDialogEvents();
|
||||||
|
|
||||||
// 检查是否有保存的连接ID,填入输入框
|
// 检查是否有保存的连接ID,填入输入框
|
||||||
const savedConnectionId = localStorage.getItem('connectionId');
|
const savedConnectionId = localStorage.getItem('connectionId');
|
||||||
if (savedConnectionId) {
|
if (savedConnectionId) {
|
||||||
|
connectionId = savedConnectionId;
|
||||||
const connectionIdInput = document.getElementById('connectionIdInput');
|
const connectionIdInput = document.getElementById('connectionIdInput');
|
||||||
if (connectionIdInput) connectionIdInput.value = savedConnectionId;
|
if (connectionIdInput) connectionIdInput.value = savedConnectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const invitePayload = getInvitePayloadFromUrl();
|
||||||
|
if (invitePayload) {
|
||||||
|
window.showCallRequest(invitePayload);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('SPA initialized, showing connect view');
|
console.log('SPA initialized, showing connect view');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing SPA:', error);
|
console.error('Error initializing SPA:', error);
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ class CallStateManager {
|
|||||||
|
|
||||||
// 监听器数组
|
// 监听器数组
|
||||||
this.listeners = [];
|
this.listeners = [];
|
||||||
|
this.socketEventHandlers = {};
|
||||||
|
this._socketInviteBound = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 订阅状态变化
|
// 订阅状态变化
|
||||||
@@ -339,10 +341,68 @@ class CallStateManager {
|
|||||||
// 创建信令实例
|
// 创建信令实例
|
||||||
this._signaling = this.useWebSocket ? new WebSocketSignaling() : new Signaling();
|
this._signaling = this.useWebSocket ? new WebSocketSignaling() : new Signaling();
|
||||||
await this._signaling.start();
|
await this._signaling.start();
|
||||||
|
this._bindSocketInviteEvents(this._signaling);
|
||||||
console.log('Signaling connected (WebSocket only, no room yet)');
|
console.log('Signaling connected (WebSocket only, no room yet)');
|
||||||
return this._signaling;
|
return this._signaling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_bindSocketInviteEvents(signaling) {
|
||||||
|
if (!signaling || this._socketInviteBound || typeof signaling.addEventListener !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
['invite-call', 'invite-accepted', 'invite-rejected', 'invite-failed'].forEach((eventName) => {
|
||||||
|
signaling.addEventListener(eventName, (event) => {
|
||||||
|
const handler = this.socketEventHandlers[eventName];
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
handler(event.detail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this._socketInviteBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSocketEvent(eventName, handler) {
|
||||||
|
this.socketEventHandlers[eventName] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActiveSignaling() {
|
||||||
|
if (this._signaling) {
|
||||||
|
return this._signaling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.renderstreaming && this.renderstreaming._signaling) {
|
||||||
|
return this.renderstreaming._signaling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInviteCall(payload) {
|
||||||
|
const signaling = this.getActiveSignaling();
|
||||||
|
if (!signaling || typeof signaling.sendInviteCall !== 'function') {
|
||||||
|
throw new Error('Invite signaling is not ready');
|
||||||
|
}
|
||||||
|
signaling.sendInviteCall(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInviteAccepted(payload) {
|
||||||
|
const signaling = this.getActiveSignaling();
|
||||||
|
if (!signaling || typeof signaling.sendInviteAccepted !== 'function') {
|
||||||
|
throw new Error('Invite signaling is not ready');
|
||||||
|
}
|
||||||
|
signaling.sendInviteAccepted(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInviteRejected(payload) {
|
||||||
|
const signaling = this.getActiveSignaling();
|
||||||
|
if (!signaling || typeof signaling.sendInviteRejected !== 'function') {
|
||||||
|
throw new Error('Invite signaling is not ready');
|
||||||
|
}
|
||||||
|
signaling.sendInviteRejected(payload);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在仅建立WebSocket连接时同步当前用户信息
|
* 在仅建立WebSocket连接时同步当前用户信息
|
||||||
* @param {{ id?: string, name?: string, avatar?: string } | null} userInfo - 用户信息
|
* @param {{ id?: string, name?: string, avatar?: string } | null} userInfo - 用户信息
|
||||||
|
|||||||
@@ -221,6 +221,18 @@ export class WebSocketSignaling extends EventTarget {
|
|||||||
case "broadcast":
|
case "broadcast":
|
||||||
this.dispatchEvent(new CustomEvent('on-message', { detail: msg.message }));
|
this.dispatchEvent(new CustomEvent('on-message', { detail: msg.message }));
|
||||||
break;
|
break;
|
||||||
|
case "invite-call":
|
||||||
|
this.dispatchEvent(new CustomEvent('invite-call', { detail: msg.data }));
|
||||||
|
break;
|
||||||
|
case "invite-accepted":
|
||||||
|
this.dispatchEvent(new CustomEvent('invite-accepted', { detail: msg.data }));
|
||||||
|
break;
|
||||||
|
case "invite-rejected":
|
||||||
|
this.dispatchEvent(new CustomEvent('invite-rejected', { detail: msg.data }));
|
||||||
|
break;
|
||||||
|
case "invite-failed":
|
||||||
|
this.dispatchEvent(new CustomEvent('invite-failed', { detail: msg.data }));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -288,4 +300,22 @@ export class WebSocketSignaling extends EventTarget {
|
|||||||
Logger.log(sendJson);
|
Logger.log(sendJson);
|
||||||
this.websocket.send(sendJson);
|
this.websocket.send(sendJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendInviteCall(payload) {
|
||||||
|
const sendJson = JSON.stringify({ type: 'invite-call', data: payload });
|
||||||
|
Logger.log(sendJson);
|
||||||
|
this.websocket.send(sendJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInviteAccepted(payload) {
|
||||||
|
const sendJson = JSON.stringify({ type: 'invite-accepted', data: payload });
|
||||||
|
Logger.log(sendJson);
|
||||||
|
this.websocket.send(sendJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInviteRejected(payload) {
|
||||||
|
const sendJson = JSON.stringify({ type: 'invite-rejected', data: payload });
|
||||||
|
Logger.log(sendJson);
|
||||||
|
this.websocket.send(sendJson);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -378,8 +378,65 @@ function onCallConnectionId(ws: WebSocket, message: any): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 处理host-userInfo消息,更新host的用户信息
|
||||||
|
* @param ws WebSocket连接实例
|
||||||
|
* @param message 消息数据
|
||||||
|
*/
|
||||||
|
function onHostUserInfo(ws: WebSocket, message: any): void {
|
||||||
|
(ws as any).userInfo = {
|
||||||
|
id: message.id || '',
|
||||||
|
name: message.name || '匿名用户',
|
||||||
|
avatar: message.avatar || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
log(LogLevel.log, 'Updated current ws userInfo:', (ws as any).userInfo);
|
||||||
|
}
|
||||||
|
function onInviteCall(ws: WebSocket, message: any): void {
|
||||||
|
const connectionId = message.connectionId as string;
|
||||||
|
const targetSocketId = message.targetSocketId as string;
|
||||||
|
const targetUserId = message.targetUserId as string;
|
||||||
|
|
||||||
|
let targetWs: WebSocket = null;
|
||||||
|
clients.forEach((_connectionIds, clientWs) => {
|
||||||
|
if (targetWs || clientWs === ws) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo = ((clientWs as any).userInfo || {}) as UserInfo;
|
||||||
|
if ((targetSocketId && (clientWs as any).socketId === targetSocketId) ||
|
||||||
|
(targetUserId && userInfo.id === targetUserId)) {
|
||||||
|
targetWs = clientWs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!targetWs) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'invite-failed',
|
||||||
|
data: {
|
||||||
|
connectionId,
|
||||||
|
reason: 'target-offline'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
log(LogLevel.warn, `invite-call target not found: socketId=${targetSocketId}, userId=${targetUserId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetWs.send(JSON.stringify({
|
||||||
|
type: 'invite-call',
|
||||||
|
data: {
|
||||||
|
connectionId,
|
||||||
|
inviterSocketId: (ws as any).socketId || '',
|
||||||
|
inviterUserId: message.inviterUserId || (((ws as any).userInfo || {}) as UserInfo).id || '',
|
||||||
|
inviterName: message.inviterName || (((ws as any).userInfo || {}) as UserInfo).name || '邀请方',
|
||||||
|
inviterAvatar: message.inviterAvatar || (((ws as any).userInfo || {}) as UserInfo).avatar || '',
|
||||||
|
targetSocketId: targetSocketId || '',
|
||||||
|
targetUserId: targetUserId || ''
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
log(LogLevel.log, `Forwarded invite-call to socketId=${targetSocketId}, userId=${targetUserId}, connectionId=${connectionId}`);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 处理广播消息请求(1对多模式)
|
* 处理广播消息请求(1对多模式)
|
||||||
* @param ws WebSocket连接实例
|
* @param ws WebSocket连接实例
|
||||||
@@ -555,4 +612,6 @@ function onMessage(ws: WebSocket, message: any): void {
|
|||||||
/**
|
/**
|
||||||
* 导出WebSocket处理器函数
|
* 导出WebSocket处理器函数
|
||||||
*/
|
*/
|
||||||
export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId, onBroadcast, onGetAllConnectionIds, onGetOnlineUsers, AddHeartbeat, RemoveHeartbeat, onMessage, isHost, broadcastToGroup, connectionGroup };
|
export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId,
|
||||||
|
onBroadcast, onGetAllConnectionIds, onGetOnlineUsers, AddHeartbeat, RemoveHeartbeat, onMessage, isHost,
|
||||||
|
broadcastToGroup, connectionGroup, onHostUserInfo, onInviteCall };
|
||||||
|
|||||||
@@ -105,6 +105,18 @@ export default class WSSignaling {
|
|||||||
case 'call-request':
|
case 'call-request':
|
||||||
handler.onCallConnectionId(ws, msg.data);
|
handler.onCallConnectionId(ws, msg.data);
|
||||||
break;
|
break;
|
||||||
|
case 'host-userInfo':
|
||||||
|
handler.onHostUserInfo(ws, msg.data);
|
||||||
|
break;
|
||||||
|
case 'invite-call':
|
||||||
|
handler.onInviteCall(ws, msg.data);
|
||||||
|
break;
|
||||||
|
// case 'invite-accepted':
|
||||||
|
// handler.onInviteAccepted(ws, msg.data);
|
||||||
|
// break;
|
||||||
|
// case 'invite-rejected':
|
||||||
|
// handler.onInviteRejected(ws, msg.data);
|
||||||
|
// break;
|
||||||
case 'on-message':
|
case 'on-message':
|
||||||
if (msg.from) msg.data.connectionId = msg.from;
|
if (msg.from) msg.data.connectionId = msg.from;
|
||||||
handler.onMessage(ws, msg.data);
|
handler.onMessage(ws, msg.data);
|
||||||
|
|||||||
Reference in New Issue
Block a user