From edf6a13a31ad55e3ab374fa7a3be295b6c0b130d Mon Sep 17 00:00:00 2001 From: Miroslav Pejic Date: Wed, 28 Sep 2022 22:50:57 +0200 Subject: [PATCH] [mirotalksfu] - add lobby --- app/src/Room.js | 7 ++ app/src/Server.js | 60 ++++++++-- public/css/Room.css | 62 ++++++++++- public/images/lobby.png | Bin 0 -> 5567 bytes public/js/Room.js | 30 +++++ public/js/RoomClient.js | 236 +++++++++++++++++++++++++++++++++++++++- public/js/Rules.js | 4 + public/sounds/lobby.wav | Bin 0 -> 24558 bytes public/views/Room.html | 40 ++++++- 9 files changed, 429 insertions(+), 10 deletions(-) create mode 100644 public/images/lobby.png create mode 100644 public/sounds/lobby.wav diff --git a/app/src/Room.js b/app/src/Room.js index 4009ca25..102991c0 100644 --- a/app/src/Room.js +++ b/app/src/Room.js @@ -14,6 +14,7 @@ module.exports = class Room { this.audioLastUpdateTime = 0; this.io = io; this._isLocked = false; + this._isLobbyEnabled = false; this._roomPassword = null; this.peers = new Map(); this.createTheRouter(); @@ -266,10 +267,16 @@ module.exports = class Room { isLocked() { return this._isLocked; } + isLobbyEnabled() { + return this._isLobbyEnabled; + } setLocked(status, password) { this._isLocked = status; this._roomPassword = password; } + setLobbyEnabled(status) { + this._isLobbyEnabled = status; + } // #################################################### // SENDER diff --git a/app/src/Server.js b/app/src/Server.js index b2e6aebf..401883a1 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -493,8 +493,41 @@ io.on('connection', (socket) => { roomList.get(socket.room_id).setLocked(false); roomList.get(socket.room_id).broadCast(socket.id, 'roomAction', data.action); break; + case 'lobbyOn': + roomList.get(socket.room_id).setLobbyEnabled(true); + roomList.get(socket.room_id).broadCast(socket.id, 'roomAction', data.action); + break; + case 'lobbyOff': + roomList.get(socket.room_id).setLobbyEnabled(false); + roomList.get(socket.room_id).broadCast(socket.id, 'roomAction', data.action); + break; + } + log.debug('Room status', { + locked: roomList.get(socket.room_id).isLocked(), + lobby: roomList.get(socket.room_id).isLobbyEnabled(), + }); + }); + + socket.on('roomLobby', (data) => { + if (!roomList.has(socket.room_id)) return; + + data.room = roomList.get(socket.room_id).toJson(); + + log.debug('Room lobby', { + peer_id: data.peer_id, + peer_name: data.peer_name, + peers_id: data.peers_id, + lobby: data.lobby_status, + broadcast: data.broadcast, + }); + + if (data.peers_id && data.broadcast) { + for (let peer_id in data.peers_id) { + roomList.get(socket.room_id).sendTo(data.peers_id[peer_id], 'roomLobby', data); + } + } else { + roomList.get(socket.room_id).sendTo(data.peer_id, 'roomLobby', data); } - log.debug('Room locked:', roomList.get(socket.room_id).isLocked()); }); socket.on('peerAction', (data) => { @@ -589,8 +622,17 @@ io.on('connection', (socket) => { if (roomList.get(socket.room_id).isLocked()) { log.debug('User rejected because room is locked'); - cb('isLocked'); - return; + return cb('isLocked'); + } + + if (roomList.get(socket.room_id).isLobbyEnabled()) { + log.debug('User waiting to join room because lobby is enabled'); + roomList.get(socket.room_id).broadCast(socket.id, 'roomLobby', { + peer_id: data.peer_info.peer_id, + peer_name: data.peer_info.peer_name, + lobby_status: 'waiting', + }); + return cb('isLobby'); } cb(roomList.get(socket.room_id).toJson()); @@ -759,8 +801,13 @@ io.on('connection', (socket) => { roomList.get(socket.room_id).removePeer(socket.id); - if (roomList.get(socket.room_id).getPeers().size === 0 && roomList.get(socket.room_id).isLocked()) { - roomList.get(socket.room_id).setLocked(false); + if (roomList.get(socket.room_id).getPeers().size === 0) { + if (roomList.get(socket.room_id).isLocked()) { + roomList.get(socket.room_id).setLocked(false); + } + if (roomList.get(socket.room_id).isLobbyEnabled()) { + roomList.get(socket.room_id).setLobbyEnabled(false); + } } roomList.get(socket.room_id).broadCast(socket.id, 'removeMe', removeMeData()); @@ -770,10 +817,9 @@ io.on('connection', (socket) => { socket.on('exitRoom', async (_, callback) => { if (!roomList.has(socket.room_id)) { - callback({ + return callback({ error: 'Not currently in a room', }); - return; } log.debug('Exit room', getPeerName()); diff --git a/public/css/Room.css b/public/css/Room.css index 61588f6b..3c885a07 100644 --- a/public/css/Room.css +++ b/public/css/Room.css @@ -724,6 +724,66 @@ button:hover { border-radius: 10px; } +/*-------------------------------------------------------------- +# Lobby users list +--------------------------------------------------------------*/ + +#lobby { + z-index: 7; + position: absolute; + margin: auto; + padding: 10px; + min-width: 320px; + background: var(--body-bg); + border: var(--border); + box-shadow: var(--box-shadow); + border-radius: 10px; + overflow: hidden; +} + +.lobby-header { + display: flex; + justify-content: space-between; + background: rgb(0, 0, 0); + border-radius: 10px; + padding: 10px; + color: #fff; + cursor: move; +} + +#lobbyUsers { + max-height: 680px; + overflow: auto; +} + +#lobbyUsers table { + border-collapse: collapse; + width: 100%; +} + +#lobbyUsers td, +#lobbyUsers th { + color: #fff; + text-align: left; + padding: 8px; +} + +#lobbyUsers tr { + border-bottom: 1px solid #444444; +} + +#lobbyUsers::-webkit-scrollbar { + width: 5px; +} + +#lobbyUsers::-webkit-scrollbar-track { + background: transparent; +} + +#lobbyUsers::-webkit-scrollbar-thumb { + background: #404040; +} + /*-------------------------------------------------------------- # Send File --------------------------------------------------------------*/ @@ -932,6 +992,6 @@ z-index: - 4 whiteboard - 5 chat - 6 settings - - 7 participants + - 7 participants/lobby - 8 send receive progress */ diff --git a/public/images/lobby.png b/public/images/lobby.png new file mode 100644 index 0000000000000000000000000000000000000000..2c270363635c202a559bc197c746b57f91d00b08 GIT binary patch literal 5567 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 literal 0 HcmV?d00001 diff --git a/public/js/Room.js b/public/js/Room.js index ee76f37a..4485ce3a 100644 --- a/public/js/Room.js +++ b/public/js/Room.js @@ -36,6 +36,7 @@ const _PEER = { videoOff: '', raiseHand: '', lowerHand: '', + acceptPeer: '', ejectPeer: '', sendFile: '', sendMsg: '', @@ -62,6 +63,7 @@ let swalBackground = 'radial-gradient(#393939, #000000)'; //'rgba(0, 0, 0, 0.7)' let rc = null; let producer = null; let participantsCount = 0; +let lobbyParticipantsCount = 0; let chatMessagesId = 0; let room_id = getRoomId(); @@ -73,6 +75,8 @@ let peer_geo = null; let peer_info = null; let isSoundEnabled = true; +let isLobbyEnabled = false; +let isLobbyOpen = false; let isEnumerateAudioDevices = false; let isEnumerateVideoDevices = false; let isAudioAllowed = false; @@ -131,6 +135,13 @@ function initClient() { setTippy('tabAspectBtn', 'Aspect', 'top'); setTippy('tabStylingBtn', 'Styling', 'top'); setTippy('tabLanguagesBtn', 'Languages', 'top'); + setTippy('lobbyAcceptAllBtn', 'Accept', 'top'); + setTippy('lobbyRejectAllBtn', 'Reject', 'top'); + setTippy( + 'switchLobby', + 'Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator', + 'right', + ); setTippy('whiteboardGhostButton', 'Toggle transparent background', 'bottom'); setTippy('wbBackgroundColorEl', 'Background color', 'bottom'); setTippy('wbDrawingColorEl', 'Drawing color', 'bottom'); @@ -655,6 +666,7 @@ function roomIsReady() { rc.makeDraggable(whiteboard, whiteboardHeader); rc.makeDraggable(sendFileDiv, imgShareSend); rc.makeDraggable(receiveFileDiv, imgShareReceive); + rc.makeDraggable(lobby, lobbyHeader); if (navigator.getDisplayMedia || navigator.mediaDevices.getDisplayMedia) { BUTTONS.main.startScreenButton && show(startScreenButton); } @@ -673,6 +685,7 @@ function roomIsReady() { show(fileShareButton); BUTTONS.settings.participantsButton && show(participantsButton); BUTTONS.settings.lockRoomButton && show(lockRoomButton); + BUTTONS.settings.lobbyButton && show(lobbyButton); BUTTONS.main.aboutButton && show(aboutButton); if (!DetectRTC.isMobileDevice) show(pinUnpinGridDiv); handleButtons(); @@ -1004,6 +1017,11 @@ function handleSelects() { switchSounds.onchange = (e) => { isSoundEnabled = e.currentTarget.checked; }; + switchLobby.onchange = (e) => { + isLobbyEnabled = e.currentTarget.checked; + rc.roomAction(isLobbyEnabled ? 'lobbyOn' : 'lobbyOff'); + rc.lobbyToggle(); + }; // styling BtnsAspectRatio.onchange = () => { setAspectRatio(BtnsAspectRatio.value); @@ -1199,6 +1217,18 @@ function handleRoomClientEvents() { show(lockRoomButton); isRoomLocked = false; }); + rc.on(RoomClient.EVENTS.lobbyOn, () => { + console.log('Room Client room lobby enabled'); + if (isRulesActive && !isPresenter) { + hide(lobbyButton); + } + sound('lobby'); + isLobbyEnabled = true; + }); + rc.on(RoomClient.EVENTS.lobbyOff, () => { + console.log('Room Client room lobby disabled'); + isLobbyEnabled = false; + }); rc.on(RoomClient.EVENTS.exitRoom, () => { console.log('Room Client leave room'); if (surveyActive) { diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index 366dd267..48c51470 100644 --- a/public/js/RoomClient.js +++ b/public/js/RoomClient.js @@ -51,6 +51,7 @@ const image = { share: '../images/share.png', exit: '../images/exit.png', feedback: '../images/feedback.png', + lobby: '../images/lobby.png', }; const mediaType = { @@ -107,6 +108,8 @@ const _EVENTS = { resumeScreen: 'resumeScreen', stopScreen: 'stopScreen', roomLock: 'roomLock', + lobbyOn: 'lobbyOn', + lobbyOff: 'lobbyOff', roomUnlock: 'roomUnlock', }; @@ -264,6 +267,11 @@ class RoomClient { console.log('00-WARNING ----> Room is Locked, Try to unlock by the password'); return this.unlockTheRoom(); } + if (room === 'isLobby') { + this.event(_EVENTS.lobbyOn); + console.log('00-WARNING ----> Room Lobby Enabled, Wait to confirm my join'); + return this.waitJoinConfirm(); + } await this.joinAllowed(room); }.bind(this), ) @@ -483,6 +491,7 @@ class RoomClient { function (data) { console.log('Remove me:', data); this.removeVideoOff(data.peer_id); + this.lobbyRemoveMe(data.peer_id); participantsCount = data.peer_counts; adaptAspectRatio(participantsCount); if (isParticipantsListOpen) getRoomParticipants(true); @@ -539,6 +548,14 @@ class RoomClient { }.bind(this), ); + this.socket.on( + 'roomLobby', + function (data) { + console.log('Room lobby:', data); + this.roomLobby(data); + }.bind(this), + ); + this.socket.on( 'peerAction', function (data) { @@ -1186,7 +1203,7 @@ class RoomClient { async consume(producer_id, peer_name, peer_info, type) { // - if (wbIsOpen && isPresenter) { + if (wbIsOpen && (!isRulesActive || isPresenter)) { console.log('Update whiteboard canvas to the participants in the room'); wbCanvasToJson(); } @@ -3210,6 +3227,14 @@ class RoomClient { this.socket.emit('roomAction', data); this.roomStatus(action); break; + case 'lobbyOn': + this.socket.emit('roomAction', data); + this.roomStatus(action); + break; + case 'lobbyOff': + this.socket.emit('roomAction', data); + this.roomStatus(action); + break; } } else { this.roomStatus(action); @@ -3227,6 +3252,14 @@ class RoomClient { this.event(_EVENTS.roomUnlock); this.userLog('info', '🔓 UNLOCKED the room', 'top-end'); break; + case 'lobbyOn': + this.event(_EVENTS.lobbyOn); + this.userLog('info', '⌛ Lobby is enabled', 'top-end'); + break; + case 'lobbyOff': + this.event(_EVENTS.lobbyOff); + this.userLog('info', '⌛ Lobby is disabled', 'top-end'); + break; } } @@ -3241,6 +3274,179 @@ class RoomClient { } } + // #################################################### + // ROOM LOBBY + // #################################################### + + roomLobby(data) { + switch (data.lobby_status) { + case 'waiting': + if (!isRulesActive || isPresenter) { + let lobbyTr = ''; + let peer_id = data.peer_id; + let peer_name = data.peer_name; + let avatarImg = getParticipantAvatar(peer_name); + let lobbyTb = this.getId('lobbyTb'); + let lobbyAccept = _PEER.acceptPeer; + let lobbyReject = _PEER.ejectPeer; + let lobbyAcceptId = `${peer_name}___${peer_id}___lobbyAccept`; + let lobbyRejectId = `${peer_name}___${peer_id}___lobbyReject`; + + lobbyTr += ` + + + ${peer_name} + + + + `; + + lobbyTb.innerHTML += lobbyTr; + lobbyParticipantsCount++; + lobbyHeaderTitle.innerText = 'Lobby users (' + lobbyParticipantsCount + ')'; + if (!isLobbyOpen) this.lobbyToggle(); + if (!this.isMobileDevice) { + setTippy(lobbyAcceptId, 'Accept', 'top'); + setTippy(lobbyRejectId, 'Reject', 'top'); + } + this.userLog('info', peer_name + ' wants to join the meeting', 'top-end'); + } + break; + case 'accept': + this.joinAllowed(data.room); + control.style.display = 'flex'; + this.msgPopup('info', 'Your join meeting was be accepted by moderator'); + break; + case 'reject': + this.sound('eject'); + Swal.fire({ + icon: 'warning', + allowOutsideClick: false, + allowEscapeKey: true, + showDenyButton: false, + showConfirmButton: true, + background: swalBackground, + title: 'Your join meeting was be rejected by moderator', + confirmButtonText: `Ok`, + showClass: { + popup: 'animate__animated animate__fadeInDown', + }, + hideClass: { + popup: 'animate__animated animate__fadeOutUp', + }, + }).then((result) => { + if (result.isConfirmed) { + this.exit(); + } + }); + break; + } + } + + lobbyAction(id, lobby_status) { + const words = id.split('___'); + const peer_name = words[0]; + const peer_id = words[1]; + const data = { + room_id: this.room_id, + peer_id: peer_id, + peer_name: peer_name, + lobby_status: lobby_status, + broadcast: false, + }; + this.socket.emit('roomLobby', data); + const trElem = this.getId(peer_id); + trElem.parentNode.removeChild(trElem); + lobbyParticipantsCount--; + lobbyHeaderTitle.innerText = 'Lobby users (' + lobbyParticipantsCount + ')'; + if (lobbyParticipantsCount == 0) this.lobbyToggle(); + } + + lobbyAcceptAll() { + if (lobbyParticipantsCount > 0) { + const data = this.lobbyGetData('accept', this.lobbyGetPeerIds()); + this.socket.emit('roomLobby', data); + this.lobbyRemoveAll(); + } else { + this.userLog('info', 'No participants in lobby detected', 'top-end'); + } + } + + lobbyRejectAll() { + if (lobbyParticipantsCount > 0) { + const data = this.lobbyGetData('reject', this.lobbyGetPeerIds()); + this.socket.emit('roomLobby', data); + this.lobbyRemoveAll(); + } else { + this.userLog('info', 'No participants in lobby detected', 'top-end'); + } + } + + lobbyRemoveAll() { + let tr = lobbyTb.getElementsByTagName('tr'); + for (let i = tr.length - 1; i >= 0; i--) { + if (tr[i].id && tr[i].id != 'lobbyAll') { + console.log('REMOVE LOBBY PEER ID ' + tr[i].id); + if (tr[i] && tr[i].parentElement) { + tr[i].parentElement.removeChild(tr[i]); + } + lobbyParticipantsCount--; + } + } + lobbyHeaderTitle.innerText = 'Lobby users (' + lobbyParticipantsCount + ')'; + if (lobbyParticipantsCount == 0) this.lobbyToggle(); + } + + lobbyRemoveMe(peer_id) { + let tr = lobbyTb.getElementsByTagName('tr'); + for (let i = tr.length - 1; i >= 0; i--) { + if (tr[i].id && tr[i].id == peer_id) { + console.log('REMOVE LOBBY PEER ID ' + tr[i].id); + if (tr[i] && tr[i].parentElement) { + tr[i].parentElement.removeChild(tr[i]); + } + lobbyParticipantsCount--; + } + } + lobbyHeaderTitle.innerText = 'Lobby users (' + lobbyParticipantsCount + ')'; + if (lobbyParticipantsCount == 0) this.lobbyToggle(); + } + + lobbyGetPeerIds() { + let peers_id = []; + let tr = lobbyTb.getElementsByTagName('tr'); + for (let i = tr.length - 1; i >= 0; i--) { + if (tr[i].id && tr[i].id != 'lobbyAll') { + peers_id.push(tr[i].id); + } + } + return peers_id; + } + + lobbyGetData(status, peers_id = []) { + return { + room_id: this.room_id, + peer_id: this.peer_id, + peer_name: this.peer_name, + peers_id: peers_id, + lobby_status: status, + broadcast: true, + }; + } + + lobbyToggle() { + if (lobbyParticipantsCount > 0 && !isLobbyOpen) { + lobby.style.display = 'block'; + lobby.style.top = '50%'; + lobby.style.left = '50%'; + isLobbyOpen = true; + this.sound('lobby'); + } else { + lobby.style.display = 'none'; + isLobbyOpen = false; + } + } + // #################################################### // HANDLE ROOM ACTION // #################################################### @@ -3298,6 +3504,34 @@ class RoomClient { }); } + waitJoinConfirm() { + this.sound('lobby'); + Swal.fire({ + allowOutsideClick: false, + allowEscapeKey: false, + showDenyButton: true, + showConfirmButton: false, + background: swalBackground, + imageUrl: image.poster, + title: 'Room has lobby enabled', + text: 'Asking to join meeting...', + confirmButtonText: `Ok`, + denyButtonText: `Leave room`, + showClass: { + popup: 'animate__animated animate__fadeInDown', + }, + hideClass: { + popup: 'animate__animated animate__fadeOutUp', + }, + }).then((result) => { + if (result.isConfirmed) { + control.style.display = 'none'; + } else { + this.exit(); + } + }); + } + // #################################################### // HANDLE AUDIO VOLUME // #################################################### diff --git a/public/js/Rules.js b/public/js/Rules.js index f5715c45..ab679d9c 100644 --- a/public/js/Rules.js +++ b/public/js/Rules.js @@ -24,6 +24,7 @@ const BUTTONS = { settings: { lockRoomButton: true, unlockRoomButton: true, + lobbyButton: true, participantsButton: true, }, producerVideo: { @@ -59,6 +60,7 @@ function handleRules(isPresenter) { if (!isPresenter) { BUTTONS.settings.lockRoomButton = false; BUTTONS.settings.unlockRoomButton = false; + BUTTONS.settings.lobbyButton = false; BUTTONS.videoOff.muteAudioButton = false; BUTTONS.videoOff.ejectButton = false; BUTTONS.consumerVideo.ejectButton = false; @@ -68,6 +70,7 @@ function handleRules(isPresenter) { } else { BUTTONS.settings.lockRoomButton = !isRoomLocked; BUTTONS.settings.unlockRoomButton = isRoomLocked; + BUTTONS.settings.lobbyButton = true; BUTTONS.videoOff.muteAudioButton = true; BUTTONS.videoOff.ejectButton = true; BUTTONS.consumerVideo.ejectButton = true; @@ -78,5 +81,6 @@ function handleRules(isPresenter) { // main. settings. BUTTONS.settings.lockRoomButton ? show(lockRoomButton) : hide(lockRoomButton); BUTTONS.settings.unlockRoomButton ? show(unlockRoomButton) : hide(unlockRoomButton); + BUTTONS.settings.lobbyButton ? show(lobbyButton) : hide(lobbyButton); //... } diff --git a/public/sounds/lobby.wav b/public/sounds/lobby.wav new file mode 100644 index 0000000000000000000000000000000000000000..f895d6d3b795a6c7cd9eb4b38794fb38cb5f39aa GIT binary patch literal 24558 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 literal 0 HcmV?d00001 diff --git a/public/views/Room.html b/public/views/Room.html index b172ce3d..951dfbf9 100644 --- a/public/views/Room.html +++ b/public/views/Room.html @@ -178,7 +178,13 @@

Unlock room

-
+ +
@@ -417,6 +423,38 @@ + +