From a8dabf73433ff1242fcb409309fd4d1bddcc7bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 21:45:16 +0200 Subject: [PATCH 01/30] If peer gets role PROMOTE_PEER, notify peer of all peers in lobby, fixes #208 --- app/src/RoomClient.js | 39 +++++++++++++++++++++++++++++++++++++++ server/lib/Room.js | 11 +++++++++++ 2 files changed, 50 insertions(+) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index d2ab9bc..075ff94 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -2052,6 +2052,8 @@ export default class RoomClient lobbyPeerActions.addLobbyPeer(peerId)); store.dispatch( roomActions.setToolbarsVisible(true)); + + this._soundNotification(); store.dispatch(requestActions.notify( { @@ -2063,6 +2065,43 @@ export default class RoomClient break; } + + case 'parkedPeers': + { + const { lobbyPeers } = notification.data; + + if (lobbyPeers.length > 0) + { + lobbyPeers.forEach((peer) => + { + store.dispatch( + lobbyPeerActions.addLobbyPeer(peer.peerId)); + store.dispatch( + lobbyPeerActions.setLobbyPeerDisplayName( + peer.displayName, + peer.peerId + ) + ); + store.dispatch( + lobbyPeerActions.setLobbyPeerPicture(peer.picture)); + }); + + store.dispatch( + roomActions.setToolbarsVisible(true)); + + this._soundNotification(); + + store.dispatch(requestActions.notify( + { + text : intl.formatMessage({ + id : 'room.newLobbyPeer', + defaultMessage : 'New participant entered the lobby' + }) + })); + } + + break; + } case 'lobby:peerClosed': { diff --git a/server/lib/Room.js b/server/lib/Room.js index c95ed93..d15ae12 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -530,6 +530,17 @@ class Room extends EventEmitter peerId : peer.id, role : newRole }, true, true); + + // Got permission to promote peers, notify peer of + // peers in lobby + if (permissionsFromRoles.PROMOTE_PEER.includes(newRole)) + { + const lobbyPeers = this._lobby.peerList(); + + lobbyPeers.length > 0 && this._notification(peer.socket, 'parkedPeers', { + lobbyPeers + }); + } }); peer.on('lostRole', ({ oldRole }) => From 881eac741aa71b28c99ee84a15bccdba2cb126de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 21:48:09 +0200 Subject: [PATCH 02/30] Remove getServerHistory, and use 'join' callback instead. --- app/src/RoomClient.js | 98 +++++++++++++++++-------------------------- server/lib/Room.js | 30 ++++--------- 2 files changed, 48 insertions(+), 80 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 075ff94..f35488f 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -844,62 +844,6 @@ export default class RoomClient } } - async getServerHistory() - { - logger.debug('getServerHistory()'); - - try - { - const { - chatHistory, - fileHistory, - lastNHistory, - locked, - lobbyPeers, - accessCode - } = await this.sendRequest('serverHistory'); - - (chatHistory.length > 0) && store.dispatch( - chatActions.addChatHistory(chatHistory)); - - (fileHistory.length > 0) && store.dispatch( - fileActions.addFileHistory(fileHistory)); - - if (lastNHistory.length > 0) - { - logger.debug('Got lastNHistory'); - - // Remove our self from list - const index = lastNHistory.indexOf(this._peerId); - - lastNHistory.splice(index, 1); - - this._spotlights.addSpeakerList(lastNHistory); - } - - locked ? - store.dispatch(roomActions.setRoomLocked()) : - store.dispatch(roomActions.setRoomUnLocked()); - - (lobbyPeers.length > 0) && lobbyPeers.forEach((peer) => - { - store.dispatch( - lobbyPeerActions.addLobbyPeer(peer.peerId)); - store.dispatch( - lobbyPeerActions.setLobbyPeerDisplayName(peer.displayName, peer.peerId)); - store.dispatch( - lobbyPeerActions.setLobbyPeerPicture(peer.picture)); - }); - - (accessCode != null) && store.dispatch( - roomActions.setAccessCode(accessCode)); - } - catch (error) - { - logger.error('getServerHistory() | failed: %o', error); - } - } - async muteMic() { logger.debug('muteMic()'); @@ -2733,7 +2677,13 @@ export default class RoomClient peers, tracker, permissionsFromRoles, - userRoles + userRoles, + chatHistory, + fileHistory, + lastNHistory, + locked, + lobbyPeers, + accessCode } = await this.sendRequest( 'join', { @@ -2788,6 +2738,38 @@ export default class RoomClient this.updateSpotlights(spotlights); }); + (chatHistory.length > 0) && store.dispatch( + chatActions.addChatHistory(chatHistory)); + + (fileHistory.length > 0) && store.dispatch( + fileActions.addFileHistory(fileHistory)); + + if (lastNHistory.length > 0) + { + logger.debug('_joinRoom() | got lastN history'); + + this._spotlights.addSpeakerList( + lastNHistory.filter((peerId) => peerId !== this._peerId) + ); + } + + locked ? + store.dispatch(roomActions.setRoomLocked()) : + store.dispatch(roomActions.setRoomUnLocked()); + + (lobbyPeers.length > 0) && lobbyPeers.forEach((peer) => + { + store.dispatch( + lobbyPeerActions.addLobbyPeer(peer.peerId)); + store.dispatch( + lobbyPeerActions.setLobbyPeerDisplayName(peer.displayName, peer.peerId)); + store.dispatch( + lobbyPeerActions.setLobbyPeerPicture(peer.picture)); + }); + + (accessCode != null) && store.dispatch( + roomActions.setAccessCode(accessCode)); + // Don't produce if explicitly requested to not to do it. if (this._produce) { @@ -2821,8 +2803,6 @@ export default class RoomClient // Clean all the existing notifications. store.dispatch(notificationActions.removeAllNotifications()); - this.getServerHistory(); - store.dispatch(requestActions.notify( { text : intl.formatMessage({ diff --git a/server/lib/Room.js b/server/lib/Room.js index d15ae12..7c3e782 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -597,13 +597,21 @@ class Room extends EventEmitter .filter((joinedPeer) => joinedPeer.id !== peer.id) .map((joinedPeer) => (joinedPeer.peerInfo)); + const lobbyPeers = this._lobby.peerList(); + cb(null, { roles : peer.roles, peers : peerInfos, tracker : config.fileTracker, authenticated : peer.authenticated, permissionsFromRoles : permissionsFromRoles, - userRoles : userRoles + userRoles : userRoles, + chatHistory : this._chatHistory, + fileHistory : this._fileHistory, + lastNHistory : this._lastN, + locked : this._locked, + lobbyPeers : lobbyPeers, + accessCode : this._accessCode }); // Mark the new Peer as joined. @@ -1078,26 +1086,6 @@ class Room extends EventEmitter break; } - case 'serverHistory': - { - // Return to sender - const lobbyPeers = this._lobby.peerList(); - - cb( - null, - { - chatHistory : this._chatHistory, - fileHistory : this._fileHistory, - lastNHistory : this._lastN, - locked : this._locked, - lobbyPeers : lobbyPeers, - accessCode : this._accessCode - } - ); - - break; - } - case 'lockRoom': { if ( From 7ed99c51d02ac9a66c254f4f65e323397fb04cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 21:48:28 +0200 Subject: [PATCH 03/30] Add missing peer.picture to lobby peerlist --- server/lib/Lobby.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index d4bbb37..708dca9 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -47,7 +47,8 @@ class Lobby extends EventEmitter return Object.values(this._peers).map((peer) => ({ peerId : peer.id, - displayName : peer.displayName + displayName : peer.displayName, + picture : peer.picture })); } From 2ad7b45ef6eee01698ee64cd566d4a95b8eaff24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 21:49:04 +0200 Subject: [PATCH 04/30] Fix translations --- app/src/RoomClient.js | 8 ++++++-- app/src/translations/cn.json | 3 +++ app/src/translations/cs.json | 3 +++ app/src/translations/de.json | 5 ++++- app/src/translations/dk.json | 3 +++ app/src/translations/el.json | 3 +++ app/src/translations/en.json | 3 +++ app/src/translations/es.json | 3 +++ app/src/translations/fr.json | 3 +++ app/src/translations/hr.json | 3 +++ app/src/translations/hu.json | 3 +++ app/src/translations/it.json | 3 +++ app/src/translations/nb.json | 3 +++ app/src/translations/pl.json | 3 +++ app/src/translations/pt.json | 3 +++ app/src/translations/ro.json | 3 +++ app/src/translations/tr.json | 5 +++++ app/src/translations/uk.json | 5 +++++ 18 files changed, 62 insertions(+), 3 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index f35488f..39de535 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -2457,7 +2457,9 @@ export default class RoomClient { text : intl.formatMessage({ id : 'roles.gotRole', - defaultMessage : `You got the role: ${role}` + defaultMessage : 'You got the role: {role}' + }, { + role }) })); } @@ -2479,7 +2481,9 @@ export default class RoomClient { text : intl.formatMessage({ id : 'roles.lostRole', - defaultMessage : `You lost the role: ${role}` + defaultMessage : 'You lost the role: {role}' + }, { + role }) })); } diff --git a/app/src/translations/cn.json b/app/src/translations/cn.json index 2d5f9bc..ce6f28b 100644 --- a/app/src/translations/cn.json +++ b/app/src/translations/cn.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "登录", "tooltip.logout": "注销", "tooltip.admitFromLobby": "从大厅允许", diff --git a/app/src/translations/cs.json b/app/src/translations/cs.json index f9ca81d..6b4c339 100644 --- a/app/src/translations/cs.json +++ b/app/src/translations/cs.json @@ -58,6 +58,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Přihlášení", "tooltip.logout": "Odhlášení", "tooltip.admitFromLobby": "Povolit uživatele z Přijímací místnosti", diff --git a/app/src/translations/de.json b/app/src/translations/de.json index a921590..e530c7c 100644 --- a/app/src/translations/de.json +++ b/app/src/translations/de.json @@ -57,7 +57,10 @@ "room.speechUnsupported": "Dein Browser unterstützt keine Spracherkennung", "room.moderatoractions": null, - "me.mutedPTT": "Du bist stummgeschalted, Halte die SPACE-Taste um zu sprechen", + "me.mutedPTT": "Du bist stummgeschalted, Halte die SPACE-Taste um zu sprechen", + + "roles.gotRole": null, + "roles.lostRole": null, "tooltip.login": "Anmelden", "tooltip.logout": "Abmelden", diff --git a/app/src/translations/dk.json b/app/src/translations/dk.json index 9296f73..af8863b 100644 --- a/app/src/translations/dk.json +++ b/app/src/translations/dk.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Log ind", "tooltip.logout": "Log ud", "tooltip.admitFromLobby": "Giv adgang fra lobbyen", diff --git a/app/src/translations/el.json b/app/src/translations/el.json index 579ba8c..cb3c3f1 100644 --- a/app/src/translations/el.json +++ b/app/src/translations/el.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Σύνδεση", "tooltip.logout": "Αποσύνδεση", "tooltip.admitFromLobby": "Admit from lobby", diff --git a/app/src/translations/en.json b/app/src/translations/en.json index a5a7920..9df2d8c 100644 --- a/app/src/translations/en.json +++ b/app/src/translations/en.json @@ -59,6 +59,9 @@ "me.mutedPTT": "You are muted, hold down SPACE-BAR to talk", + "roles.gotRole": "You got the role: {role}", + "roles.lostRole": "You lost the role: {role}", + "tooltip.login": "Log in", "tooltip.logout": "Log out", "tooltip.admitFromLobby": "Admit from lobby", diff --git a/app/src/translations/es.json b/app/src/translations/es.json index a7ef07c..0974d89 100644 --- a/app/src/translations/es.json +++ b/app/src/translations/es.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Entrar", "tooltip.logout": "Salir", "tooltip.admitFromLobby": "Admitir desde la sala de espera", diff --git a/app/src/translations/fr.json b/app/src/translations/fr.json index f4e8bf0..f51959c 100644 --- a/app/src/translations/fr.json +++ b/app/src/translations/fr.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Connexion", "tooltip.logout": "Déconnexion", "tooltip.admitFromLobby": "Autorisé depuis la salle d'attente", diff --git a/app/src/translations/hr.json b/app/src/translations/hr.json index 8e90529..0126080 100644 --- a/app/src/translations/hr.json +++ b/app/src/translations/hr.json @@ -59,6 +59,9 @@ "me.mutedPTT": "Utišani ste, pritisnite i držite SPACE tipku za razgovor", + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Prijava", "tooltip.logout": "Odjava", "tooltip.admitFromLobby": "Pusti iz predvorja", diff --git a/app/src/translations/hu.json b/app/src/translations/hu.json index 1afaeea..a02ad2c 100644 --- a/app/src/translations/hu.json +++ b/app/src/translations/hu.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Belépés", "tooltip.logout": "Kilépés", "tooltip.admitFromLobby": "Beenegdem az előszobából", diff --git a/app/src/translations/it.json b/app/src/translations/it.json index fb61222..d9abab0 100644 --- a/app/src/translations/it.json +++ b/app/src/translations/it.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Log in", "tooltip.logout": "Log out", "tooltip.admitFromLobby": "Ammetti dalla lobby", diff --git a/app/src/translations/nb.json b/app/src/translations/nb.json index 275ce5a..146bf9e 100644 --- a/app/src/translations/nb.json +++ b/app/src/translations/nb.json @@ -59,6 +59,9 @@ "me.mutedPTT": "Du er dempet, hold nede SPACE for å snakke", + "roles.gotRole": "Du fikk rollen: {role}", + "roles.lostRole": "Du mistet rollen: {role}", + "tooltip.login": "Logg in", "tooltip.logout": "Logg ut", "tooltip.admitFromLobby": "Slipp inn fra lobby", diff --git a/app/src/translations/pl.json b/app/src/translations/pl.json index f334513..97034e1 100644 --- a/app/src/translations/pl.json +++ b/app/src/translations/pl.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Zaloguj", "tooltip.logout": "Wyloguj", "tooltip.admitFromLobby": "Przejście z poczekalni", diff --git a/app/src/translations/pt.json b/app/src/translations/pt.json index 0f8b940..7149910 100644 --- a/app/src/translations/pt.json +++ b/app/src/translations/pt.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Entrar", "tooltip.logout": "Sair", "tooltip.admitFromLobby": "Admitir da sala de espera", diff --git a/app/src/translations/ro.json b/app/src/translations/ro.json index a28a9b5..5fce442 100644 --- a/app/src/translations/ro.json +++ b/app/src/translations/ro.json @@ -59,6 +59,9 @@ "me.mutedPTT": null, + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Intră în cont", "tooltip.logout": "Deconectare", "tooltip.admitFromLobby": "Admite din hol", diff --git a/app/src/translations/tr.json b/app/src/translations/tr.json index 99f2556..835013e 100644 --- a/app/src/translations/tr.json +++ b/app/src/translations/tr.json @@ -57,6 +57,11 @@ "room.speechUnsupported": null, "room.moderatoractions": null, + "me.mutedPTT": null, + + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Giriş", "tooltip.logout": "Çıkış", "tooltip.admitFromLobby": "Lobiden içeri al", diff --git a/app/src/translations/uk.json b/app/src/translations/uk.json index bd59894..f3d6aea 100644 --- a/app/src/translations/uk.json +++ b/app/src/translations/uk.json @@ -57,6 +57,11 @@ "room.speechUnsupported": null, "room.moderatoractions": null, + "me.mutedPTT": null, + + "roles.gotRole": null, + "roles.lostRole": null, + "tooltip.login": "Увійти", "tooltip.logout": "Вихід", "tooltip.admitFromLobby": "Вхід із залу очікувань", From 87b114fcccf53c0684d031c3dfb4e1a80181c5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 21:49:40 +0200 Subject: [PATCH 05/30] Styling and lint --- app/src/components/Controls/TopBar.js | 89 ++++++++++--------- app/src/components/JoinDialog.js | 2 +- .../components/MeetingDrawer/Chat/Message.js | 2 +- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js index 277507b..7f4be52 100644 --- a/app/src/components/Controls/TopBar.js +++ b/app/src/components/Controls/TopBar.js @@ -81,6 +81,10 @@ const styles = (theme) => margin : theme.spacing(1, 0), padding : theme.spacing(0, 1) }, + disabledButton : + { + margin : theme.spacing(1, 0) + }, green : { color : 'rgba(0, 153, 0, 1)' @@ -270,32 +274,34 @@ const TopBar = (props) => - - { - if (room.locked) + + { - roomClient.unlockRoom(); + if (room.locked) + { + roomClient.unlockRoom(); + } + else + { + roomClient.lockRoom(); + } + }} + > + { room.locked ? + + : + } - else - { - roomClient.lockRoom(); - } - }} - > - { room.locked ? - - : - - } - + + { lobbyPeers.length > 0 && defaultMessage : 'Show lobby' })} > - setLockDialogOpen(!room.lockDialogOpen)} - > - + setLockDialogOpen(!room.lockDialogOpen)} > - - - + + + + + } { loginEnabled && @@ -339,7 +348,7 @@ const TopBar = (props) => { myPicture ? : - + } diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js index 1f270c6..a8493db 100644 --- a/app/src/components/JoinDialog.js +++ b/app/src/components/JoinDialog.js @@ -149,7 +149,7 @@ const DialogTitle = withStyles(styles)((props) => : } diff --git a/app/src/components/MeetingDrawer/Chat/Message.js b/app/src/components/MeetingDrawer/Chat/Message.js index 724a053..b770689 100644 --- a/app/src/components/MeetingDrawer/Chat/Message.js +++ b/app/src/components/MeetingDrawer/Chat/Message.js @@ -94,7 +94,7 @@ const Message = (props) => { self ? intl.formatMessage({ - id : 'room.me', + id : 'room.me', defaultMessage : 'Me' }) : From 36b7e45f05fe615eda27d0fe823f6806a5195f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 22:04:14 +0200 Subject: [PATCH 06/30] Clean up lifecycle methods --- app/src/components/MeetingViews/Filmstrip.js | 28 ++++++++----------- app/src/components/PeerAudio/PeerAudio.js | 12 ++++---- .../components/VideoContainers/VideoView.js | 9 ++++-- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/app/src/components/MeetingViews/Filmstrip.js b/app/src/components/MeetingViews/Filmstrip.js index 2ed11c6..9469672 100644 --- a/app/src/components/MeetingViews/Filmstrip.js +++ b/app/src/components/MeetingViews/Filmstrip.js @@ -172,27 +172,21 @@ class Filmstrip extends React.PureComponent window.removeEventListener('resize', this.updateDimensions); } - componentWillUpdate(nextProps) - { - if (nextProps !== this.props) - { - if ( - nextProps.activeSpeakerId != null && - nextProps.activeSpeakerId !== this.props.myId - ) - { - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ - lastSpeaker : nextProps.activeSpeakerId - }); - } - } - } - componentDidUpdate(prevProps) { if (prevProps !== this.props) { + if ( + this.props.activeSpeakerId != null && + this.props.activeSpeakerId !== this.props.myId + ) + { + // eslint-disable-next-line react/no-did-update-set-state + this.setState({ + lastSpeaker : this.props.activeSpeakerId + }); + } + this.updateDimensions(); } } diff --git a/app/src/components/PeerAudio/PeerAudio.js b/app/src/components/PeerAudio/PeerAudio.js index c5a4396..b4be0f7 100644 --- a/app/src/components/PeerAudio/PeerAudio.js +++ b/app/src/components/PeerAudio/PeerAudio.js @@ -31,13 +31,15 @@ export default class PeerAudio extends React.PureComponent this._setOutputDevice(audioOutputDevice); } - // eslint-disable-next-line camelcase - UNSAFE_componentWillReceiveProps(nextProps) + componentDidUpdate(prevProps) { - const { audioTrack, audioOutputDevice } = nextProps; + if (prevProps !== this.props) + { + const { audioTrack, audioOutputDevice } = this.props; - this._setTrack(audioTrack); - this._setOutputDevice(audioOutputDevice); + this._setTrack(audioTrack); + this._setOutputDevice(audioOutputDevice); + } } _setTrack(audioTrack) diff --git a/app/src/components/VideoContainers/VideoView.js b/app/src/components/VideoContainers/VideoView.js index e6e4fa3..fd84bae 100644 --- a/app/src/components/VideoContainers/VideoView.js +++ b/app/src/components/VideoContainers/VideoView.js @@ -345,11 +345,14 @@ class VideoView extends React.PureComponent } } - componentWillUpdate() + componentDidUpdate(prevProps) { - const { videoTrack, audioTrack } = this.props; + if (prevProps !== this.props) + { + const { videoTrack, audioTrack } = this.props; - this._setTracks(videoTrack, audioTrack); + this._setTracks(videoTrack, audioTrack); + } } _setTracks(videoTrack, audioTrack) From 695d366e596ae37f8a07d3a12fa9c4fd65c930ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 23:28:42 +0200 Subject: [PATCH 07/30] Fix network status icon for speaker in filmstrip view. --- app/src/components/Containers/SpeakerPeer.js | 59 ++++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/app/src/components/Containers/SpeakerPeer.js b/app/src/components/Containers/SpeakerPeer.js index fa3300b..3edb7c0 100644 --- a/app/src/components/Containers/SpeakerPeer.js +++ b/app/src/components/Containers/SpeakerPeer.js @@ -91,16 +91,6 @@ const SpeakerPeer = (props) => !screenConsumer.remotelyPaused ); - let videoProfile; - - if (webcamConsumer) - videoProfile = webcamConsumer.profile; - - let screenProfile; - - if (screenConsumer) - screenProfile = screenConsumer.profile; - const spacingStyle = { 'margin' : spacing @@ -134,11 +124,27 @@ const SpeakerPeer = (props) => peer={peer} displayName={peer.displayName} showPeerInfo - videoTrack={webcamConsumer ? webcamConsumer.track : null} + consumerSpatialLayers={webcamConsumer ? webcamConsumer.spatialLayers : null} + consumerTemporalLayers={webcamConsumer ? webcamConsumer.temporalLayers : null} + consumerCurrentSpatialLayer={ + webcamConsumer ? webcamConsumer.currentSpatialLayer : null + } + consumerCurrentTemporalLayer={ + webcamConsumer ? webcamConsumer.currentTemporalLayer : null + } + consumerPreferredSpatialLayer={ + webcamConsumer ? webcamConsumer.preferredSpatialLayer : null + } + consumerPreferredTemporalLayer={ + webcamConsumer ? webcamConsumer.preferredTemporalLayer : null + } + videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'} + videoTrack={webcamConsumer && webcamConsumer.track} videoVisible={videoVisible} - videoProfile={videoProfile} - audioCodec={micConsumer ? micConsumer.codec : null} - videoCodec={webcamConsumer ? webcamConsumer.codec : null} + audioCodec={micConsumer && micConsumer.codec} + videoCodec={webcamConsumer && webcamConsumer.codec} + audioScore={micConsumer ? micConsumer.score : null} + videoScore={webcamConsumer ? webcamConsumer.score : null} > @@ -165,10 +171,29 @@ const SpeakerPeer = (props) => } From d6d962e3794e17e7b288db290ec243d345f392b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 23:32:13 +0200 Subject: [PATCH 08/30] Fix filmstrip. Need to fix buttons inside videoview if screen is too small. Fixes #231, ref #115 --- app/src/components/MeetingViews/Filmstrip.js | 84 +++++++++++++++----- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/app/src/components/MeetingViews/Filmstrip.js b/app/src/components/MeetingViews/Filmstrip.js index 9469672..f78e5b5 100644 --- a/app/src/components/MeetingViews/Filmstrip.js +++ b/app/src/components/MeetingViews/Filmstrip.js @@ -12,6 +12,12 @@ import Peer from '../Containers/Peer'; import SpeakerPeer from '../Containers/SpeakerPeer'; import Grid from '@material-ui/core/Grid'; +const RATIO = 1.334; +const PADDING_V = 40; +const PADDING_H = 0; +const FILMSTRING_PADDING_V = 10; +const FILMSTRING_PADDING_H = 0; + const styles = () => ({ root : @@ -20,24 +26,22 @@ const styles = () => width : '100%', display : 'grid', gridTemplateColumns : '1fr', - gridTemplateRows : '1.6fr minmax(0, 0.4fr)' + gridTemplateRows : '1fr 0.25fr' }, speaker : { - gridArea : '1 / 1 / 2 / 2', + gridArea : '1 / 1 / 1 / 1', display : 'flex', justifyContent : 'center', - alignItems : 'center', - paddingTop : 40 + alignItems : 'center' }, filmStrip : { - gridArea : '2 / 1 / 3 / 2' + gridArea : '2 / 1 / 2 / 1' }, filmItem : { display : 'flex', - marginLeft : '6px', border : 'var(--peer-border)', '&.selected' : { @@ -47,6 +51,16 @@ const styles = () => { opacity : '0.6' } + }, + hiddenToolBar : + { + paddingTop : 0, + transition : 'padding .5s' + }, + showingToolBar : + { + paddingTop : 60, + transition : 'padding .5s' } }); @@ -58,6 +72,8 @@ class Filmstrip extends React.PureComponent this.resizeTimeout = null; + this.rootContainer = React.createRef(); + this.activePeerContainer = React.createRef(); this.filmStripContainer = React.createRef(); @@ -105,24 +121,35 @@ class Filmstrip extends React.PureComponent { const newState = {}; + const root = this.rootContainer.current; + + const availableWidth = root.clientWidth; + // Grid is: + // 4/5 speaker + // 1/5 filmstrip + const availableSpeakerHeight = (root.clientHeight * 0.8) - + (this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING_V : PADDING_H); + + const availableFilmstripHeight = root.clientHeight * 0.2; + const speaker = this.activePeerContainer.current; if (speaker) { - let speakerWidth = (speaker.clientWidth - 100); + let speakerWidth = (availableWidth - PADDING_H); - let speakerHeight = (speakerWidth / 4) * 3; + let speakerHeight = speakerWidth / RATIO; if (this.isSharingCamera(this.getActivePeerId())) { speakerWidth /= 2; - speakerHeight = (speakerWidth / 4) * 3; + speakerHeight = speakerWidth / RATIO; } - if (speakerHeight > (speaker.clientHeight - 60)) + if (speakerHeight > (availableSpeakerHeight - PADDING_V)) { - speakerHeight = (speaker.clientHeight - 60); - speakerWidth = (speakerHeight / 3) * 4; + speakerHeight = (availableSpeakerHeight - PADDING_V); + speakerWidth = speakerHeight * RATIO; } newState.speakerWidth = speakerWidth; @@ -133,14 +160,18 @@ class Filmstrip extends React.PureComponent if (filmStrip) { - let filmStripHeight = filmStrip.clientHeight - 10; + let filmStripHeight = availableFilmstripHeight - FILMSTRING_PADDING_V; - let filmStripWidth = (filmStripHeight / 3) * 4; + let filmStripWidth = filmStripHeight * RATIO; - if (filmStripWidth * this.props.boxes > (filmStrip.clientWidth - 50)) + if ( + (filmStripWidth * this.props.boxes) > + (availableWidth - FILMSTRING_PADDING_H) + ) { - filmStripWidth = (filmStrip.clientWidth - 50) / this.props.boxes; - filmStripHeight = (filmStripWidth / 4) * 3; + filmStripWidth = (availableWidth - FILMSTRING_PADDING_H) / + this.props.boxes; + filmStripHeight = filmStripWidth / RATIO; } newState.filmStripWidth = filmStripWidth; @@ -199,6 +230,8 @@ class Filmstrip extends React.PureComponent myId, advancedMode, spotlights, + toolbarsVisible, + permanentTopBar, classes } = this.props; @@ -217,7 +250,14 @@ class Filmstrip extends React.PureComponent }; return ( -
+
{ peers[activePeerId] && consumers : state.consumers, myId : state.me.id, spotlights : state.room.spotlights, - boxes : videoBoxesSelector(state) + boxes : videoBoxesSelector(state), + toolbarsVisible : state.room.toolbarsVisible, + permanentTopBar : state.settings.permanentTopBar }; }; @@ -316,6 +360,8 @@ export default withRoomContext(connect( return ( prev.room.activeSpeakerId === next.room.activeSpeakerId && prev.room.selectedPeerId === next.room.selectedPeerId && + prev.room.toolbarsVisible === next.room.toolbarsVisible && + prev.settings.permanentTopBar === next.settings.permanentTopBar && prev.peers === next.peers && prev.consumers === next.consumers && prev.room.spotlights === next.room.spotlights && From 3e858ae6cd530a7370c782df89c5640f063ea40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 23:33:39 +0200 Subject: [PATCH 09/30] Fix network indicator for fullscreen video. Fixes #218 --- .../VideoContainers/FullScreenView.js | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/app/src/components/VideoContainers/FullScreenView.js b/app/src/components/VideoContainers/FullScreenView.js index 05d1484..0657960 100644 --- a/app/src/components/VideoContainers/FullScreenView.js +++ b/app/src/components/VideoContainers/FullScreenView.js @@ -96,11 +96,6 @@ const FullScreenView = (props) => !consumer.remotelyPaused ); - let consumerProfile; - - if (consumer) - consumerProfile = consumer.profile; - return (
@@ -121,9 +116,25 @@ const FullScreenView = (props) =>
); From c0507daabca16c17218caafd5a37de7a214a5ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 23:33:59 +0200 Subject: [PATCH 10/30] Change in lifecycle method --- app/src/components/VideoContainers/FullView.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/components/VideoContainers/FullView.js b/app/src/components/VideoContainers/FullView.js index 4e53a89..c46b67c 100644 --- a/app/src/components/VideoContainers/FullView.js +++ b/app/src/components/VideoContainers/FullView.js @@ -81,11 +81,14 @@ class FullView extends React.PureComponent this._setTracks(videoTrack); } - componentDidUpdate() + componentDidUpdate(prevProps) { - const { videoTrack } = this.props; + if (prevProps !== this.props) + { + const { videoTrack } = this.props; - this._setTracks(videoTrack); + this._setTracks(videoTrack); + } } _setTracks(videoTrack) From f28abc9c74dc914bb3669fa5039d44b8149e23aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 23:34:29 +0200 Subject: [PATCH 11/30] Fix videoview in videowindow --- app/src/components/VideoWindow/VideoWindow.js | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/app/src/components/VideoWindow/VideoWindow.js b/app/src/components/VideoWindow/VideoWindow.js index e478070..7dcf970 100644 --- a/app/src/components/VideoWindow/VideoWindow.js +++ b/app/src/components/VideoWindow/VideoWindow.js @@ -23,18 +23,29 @@ const VideoWindow = (props) => !consumer.remotelyPaused ); - let consumerProfile; - - if (consumer) - consumerProfile = consumer.profile; - return ( ); From e061cce53f654739aed2b0c4f43e9f5bab823704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 1 May 2020 23:49:16 +0200 Subject: [PATCH 12/30] Add raised hand status to peerInfo. This will give joining peers raise hand status. Ref #40 --- server/lib/Peer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/lib/Peer.js b/server/lib/Peer.js index 2491603..70b2394 100644 --- a/server/lib/Peer.js +++ b/server/lib/Peer.js @@ -336,7 +336,8 @@ class Peer extends EventEmitter id : this.id, displayName : this.displayName, picture : this.picture, - roles : this.roles + roles : this.roles, + raisedHand : this.raisedHand }; return peerInfo; From e33c1f7c03a900de12790236008aaf281623dc73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Sat, 2 May 2020 10:47:57 +0200 Subject: [PATCH 13/30] Clean up raised hand naming. Only needs UI now. Ref #40 --- app/src/RoomClient.js | 25 +++++++++++++------ app/src/__tests__/Room.spec.js | 4 +-- app/src/actions/meActions.js | 8 +++--- app/src/actions/peerActions.js | 6 ++--- .../MeetingDrawer/ParticipantList/ListMe.js | 4 +-- .../MeetingDrawer/ParticipantList/ListPeer.js | 10 ++++---- app/src/reducers/me.js | 12 ++++----- app/src/reducers/peers.js | 6 ++--- server/lib/Room.js | 4 +-- 9 files changed, 44 insertions(+), 35 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 39de535..d74bfd0 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -1505,30 +1505,30 @@ export default class RoomClient } } - async sendRaiseHandState(state) + async setRaisedHand(raisedHand) { - logger.debug('sendRaiseHandState: ', state); + logger.debug('setRaisedHand: ', raisedHand); store.dispatch( - meActions.setMyRaiseHandStateInProgress(true)); + meActions.setRaisedHandInProgress(true)); try { - await this.sendRequest('raiseHand', { raiseHandState: state }); + await this.sendRequest('raisedHand', { raisedHand }); store.dispatch( - meActions.setMyRaiseHandState(state)); + meActions.setRaisedHand(raisedHand)); } catch (error) { - logger.error('sendRaiseHandState() | failed: %o', error); + logger.error('setRaisedHand() | [error:"%o"]', error); // We need to refresh the component for it to render changed state - store.dispatch(meActions.setMyRaiseHandState(!state)); + store.dispatch(meActions.setRaisedHand(!raisedHand)); } store.dispatch( - meActions.setMyRaiseHandStateInProgress(false)); + meActions.setRaisedHandInProgress(false)); } async setMaxSendingSpatialLayer(spatialLayer) @@ -2205,6 +2205,15 @@ export default class RoomClient break; } + case 'raisedHand': + { + const { peerId, raisedHand } = notification.data; + + store.dispatch(peerActions.setPeerRaisedHand(peerId, raisedHand)); + + break; + } + case 'chatMessage': { const { peerId, chatMessage } = notification.data; diff --git a/app/src/__tests__/Room.spec.js b/app/src/__tests__/Room.spec.js index 3c802d3..bd62322 100644 --- a/app/src/__tests__/Room.spec.js +++ b/app/src/__tests__/Room.spec.js @@ -42,8 +42,8 @@ beforeEach(() => loggedIn : false, loginEnabled : true, picture : null, - raiseHand : false, - raiseHandInProgress : false, + raisedHand : false, + raisedHandInProgress : false, screenShareInProgress : false, webcamDevices : null, webcamInProgress : false diff --git a/app/src/actions/meActions.js b/app/src/actions/meActions.js index 9572dcb..7fb34ea 100644 --- a/app/src/actions/meActions.js +++ b/app/src/actions/meActions.js @@ -63,9 +63,9 @@ export const setWebcamDevices = (devices) => payload : { devices } }); -export const setMyRaiseHandState = (flag) => +export const setRaisedHand = (flag) => ({ - type : 'SET_MY_RAISE_HAND_STATE', + type : 'SET_RAISED_HAND', payload : { flag } }); @@ -93,9 +93,9 @@ export const setScreenShareInProgress = (flag) => payload : { flag } }); -export const setMyRaiseHandStateInProgress = (flag) => +export const setRaisedHandInProgress = (flag) => ({ - type : 'SET_MY_RAISE_HAND_STATE_IN_PROGRESS', + type : 'SET_RAISED_HAND_IN_PROGRESS', payload : { flag } }); diff --git a/app/src/actions/peerActions.js b/app/src/actions/peerActions.js index dc41568..c1c3842 100644 --- a/app/src/actions/peerActions.js +++ b/app/src/actions/peerActions.js @@ -34,10 +34,10 @@ export const setPeerScreenInProgress = (peerId, flag) => payload : { peerId, flag } }); -export const setPeerRaiseHandState = (peerId, raiseHandState) => +export const setPeerRaisedHand = (peerId, raisedHand) => ({ - type : 'SET_PEER_RAISE_HAND_STATE', - payload : { peerId, raiseHandState } + type : 'SET_PEER_RAISED_HAND', + payload : { peerId, raisedHand } }); export const setPeerPicture = (peerId, picture) => diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListMe.js b/app/src/components/MeetingDrawer/ParticipantList/ListMe.js index 304cbb9..cadfed9 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListMe.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListMe.js @@ -63,7 +63,7 @@ const styles = (theme) => { opacity : 1 }, - '&.raise-hand' : + '&.raisedHand' : { backgroundImage : `url(${HandIcon})`, opacity : 1 @@ -92,7 +92,7 @@ const ListMe = (props) =>
{ me.raisedHand && -
+
}
diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js index a563d9a..7578268 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js @@ -82,7 +82,7 @@ const styles = (theme) => { opacity : 0.2 }, - '&.raise-hand' : + '&.raisedHand' : { backgroundImage : `url(${HandIcon})` } @@ -140,12 +140,12 @@ const ListPeer = (props) => {peer.displayName}
- { peer.raiseHandState && + { peer.raisedHand &&
return { ...state, screenShareInProgress: flag }; } - case 'SET_MY_RAISE_HAND_STATE': + case 'SET_RAISED_HAND': { const { flag } = action.payload; - return { ...state, raiseHand: flag }; + return { ...state, raisedHand: flag }; } - case 'SET_MY_RAISE_HAND_STATE_IN_PROGRESS': + case 'SET_RAISED_HAND_IN_PROGRESS': { const { flag } = action.payload; - return { ...state, raiseHandInProgress: flag }; + return { ...state, raisedHandInProgress: flag }; } case 'SET_DISPLAY_NAME_IN_PROGRESS': diff --git a/app/src/reducers/peers.js b/app/src/reducers/peers.js index e3d207d..46fb99e 100644 --- a/app/src/reducers/peers.js +++ b/app/src/reducers/peers.js @@ -20,8 +20,8 @@ const peer = (state = {}, action) => case 'SET_PEER_KICK_IN_PROGRESS': return { ...state, peerKickInProgress: action.payload.flag }; - case 'SET_PEER_RAISE_HAND_STATE': - return { ...state, raiseHandState: action.payload.raiseHandState }; + case 'SET_PEER_RAISED_HAND': + return { ...state, raisedHand: action.payload.raisedHand }; case 'ADD_CONSUMER': { @@ -86,7 +86,7 @@ const peers = (state = {}, action) => case 'SET_PEER_VIDEO_IN_PROGRESS': case 'SET_PEER_AUDIO_IN_PROGRESS': case 'SET_PEER_SCREEN_IN_PROGRESS': - case 'SET_PEER_RAISE_HAND_STATE': + case 'SET_PEER_RAISED_HAND': case 'SET_PEER_PICTURE': case 'ADD_CONSUMER': case 'ADD_PEER_ROLE': diff --git a/server/lib/Room.js b/server/lib/Room.js index 7c3e782..fc9b2e9 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -1251,14 +1251,14 @@ class Room extends EventEmitter break; } - case 'raiseHand': + case 'raisedHand': { const { raisedHand } = request.data; peer.raisedHand = raisedHand; // Spread to others - this._notification(peer.socket, 'raiseHand', { + this._notification(peer.socket, 'raisedHand', { peerId : peer.id, raisedHand : raisedHand }, true); From d76682b6c807024cdd1cca9ae2c82748573ee3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Sat, 2 May 2020 13:34:19 +0200 Subject: [PATCH 14/30] Added raise hand button back to UI, fixes #40 --- .../MeetingDrawer/ParticipantList/ListMe.js | 100 +++----- .../MeetingDrawer/ParticipantList/ListPeer.js | 242 +++++++----------- app/src/images/icon-hand-black.svg | 26 -- app/src/images/icon-hand-white.svg | 26 -- app/src/translations/cn.json | 1 + app/src/translations/cs.json | 1 + app/src/translations/de.json | 1 + app/src/translations/dk.json | 1 + app/src/translations/el.json | 1 + app/src/translations/en.json | 1 + app/src/translations/es.json | 1 + app/src/translations/fr.json | 1 + app/src/translations/hr.json | 1 + app/src/translations/hu.json | 1 + app/src/translations/it.json | 1 + app/src/translations/nb.json | 1 + app/src/translations/pl.json | 1 + app/src/translations/pt.json | 1 + app/src/translations/ro.json | 1 + app/src/translations/tr.json | 1 + app/src/translations/uk.json | 1 + 21 files changed, 150 insertions(+), 261 deletions(-) delete mode 100644 app/src/images/icon-hand-black.svg delete mode 100644 app/src/images/icon-hand-white.svg diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListMe.js b/app/src/components/MeetingDrawer/ParticipantList/ListMe.js index cadfed9..0bc8446 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListMe.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListMe.js @@ -1,79 +1,50 @@ import React from 'react'; import { connect } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; -import classnames from 'classnames'; +import { withRoomContext } from '../../../RoomContext'; import PropTypes from 'prop-types'; import * as appPropTypes from '../../appPropTypes'; +import { useIntl } from 'react-intl'; +import IconButton from '@material-ui/core/IconButton'; +import PanIcon from '@material-ui/icons/PanTool'; import EmptyAvatar from '../../../images/avatar-empty.jpeg'; -import HandIcon from '../../../images/icon-hand-white.svg'; const styles = (theme) => ({ root : { - padding : theme.spacing(1), width : '100%', overflow : 'hidden', cursor : 'auto', - display : 'flex' - }, - listPeer : - { - display : 'flex' + display : 'flex', + padding : theme.spacing(1) }, avatar : { borderRadius : '50%', - height : '2rem' + height : '2rem', + marginTop : theme.spacing(1) }, peerInfo : { fontSize : '1rem', - border : 'none', display : 'flex', paddingLeft : theme.spacing(1), flexGrow : 1, alignItems : 'center' }, - indicators : + green : { - left : 0, - top : 0, - display : 'flex', - flexDirection : 'row', - justifyContent : 'flex-start', - alignItems : 'center', - transition : 'opacity 0.3s' - }, - icon : - { - flex : '0 0 auto', - margin : '0.3rem', - borderRadius : 2, - backgroundPosition : 'center', - backgroundSize : '75%', - backgroundRepeat : 'no-repeat', - backgroundColor : 'rgba(0, 0, 0, 0.5)', - transitionProperty : 'opacity, background-color', - transitionDuration : '0.15s', - width : 'var(--media-control-button-size)', - height : 'var(--media-control-button-size)', - opacity : 0.85, - '&:hover' : - { - opacity : 1 - }, - '&.raisedHand' : - { - backgroundImage : `url(${HandIcon})`, - opacity : 1 - } + color : 'rgba(0, 153, 0, 1)' } }); const ListMe = (props) => { + const intl = useIntl(); + const { + roomClient, me, settings, classes @@ -82,29 +53,38 @@ const ListMe = (props) => const picture = me.picture || EmptyAvatar; return ( -
  • -
    - My avatar +
    + My avatar -
    - {settings.displayName} -
    - -
    - { me.raisedHand && -
    - } -
    +
    + {settings.displayName}
    -
  • + + { + e.stopPropagation(); + + roomClient.setRaisedHand(!me.raisedHand); + }} + > + + +
    ); }; ListMe.propTypes = { - me : appPropTypes.Me.isRequired, - settings : PropTypes.object.isRequired, - classes : PropTypes.object.isRequired + roomClient : PropTypes.object.isRequired, + me : appPropTypes.Me.isRequired, + settings : PropTypes.object.isRequired, + classes : PropTypes.object.isRequired }; const mapStateToProps = (state) => ({ @@ -112,7 +92,7 @@ const mapStateToProps = (state) => ({ settings : state.settings }); -export default connect( +export default withRoomContext(connect( mapStateToProps, null, null, @@ -125,4 +105,4 @@ export default connect( ); } } -)(withStyles(styles)(ListMe)); +)(withStyles(styles)(ListMe))); diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js index 7578268..d4b1409 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js @@ -3,7 +3,6 @@ import { connect } from 'react-redux'; import { makePeerConsumerSelector } from '../../Selectors'; import { withStyles } from '@material-ui/core/styles'; import PropTypes from 'prop-types'; -import classnames from 'classnames'; import * as appPropTypes from '../../appPropTypes'; import { withRoomContext } from '../../../RoomContext'; import { useIntl } from 'react-intl'; @@ -16,31 +15,26 @@ import ScreenIcon from '@material-ui/icons/ScreenShare'; import ScreenOffIcon from '@material-ui/icons/StopScreenShare'; import ExitIcon from '@material-ui/icons/ExitToApp'; import EmptyAvatar from '../../../images/avatar-empty.jpeg'; -import HandIcon from '../../../images/icon-hand-white.svg'; +import PanIcon from '@material-ui/icons/PanTool'; const styles = (theme) => ({ root : { - padding : theme.spacing(1), width : '100%', overflow : 'hidden', cursor : 'auto', display : 'flex' }, - listPeer : - { - display : 'flex' - }, avatar : { borderRadius : '50%', - height : '2rem' + height : '2rem', + marginTop : theme.spacing(1) }, peerInfo : { fontSize : '1rem', - border : 'none', display : 'flex', paddingLeft : theme.spacing(1), flexGrow : 1, @@ -48,52 +42,12 @@ const styles = (theme) => }, indicators : { - left : 0, - top : 0, - display : 'flex', - flexDirection : 'row', - justifyContent : 'flex-start', - alignItems : 'center', - transition : 'opacity 0.3s' + display : 'flex', + padding : theme.spacing(1.5) }, - icon : + green : { - flex : '0 0 auto', - margin : '0.3rem', - borderRadius : 2, - backgroundPosition : 'center', - backgroundSize : '75%', - backgroundRepeat : 'no-repeat', - backgroundColor : 'rgba(0, 0, 0, 0.5)', - transitionProperty : 'opacity, background-color', - transitionDuration : '0.15s', - width : 'var(--media-control-button-size)', - height : 'var(--media-control-button-size)', - opacity : 0.85, - '&:hover' : - { - opacity : 1 - }, - '&.on' : - { - opacity : 1 - }, - '&.off' : - { - opacity : 0.2 - }, - '&.raisedHand' : - { - backgroundImage : `url(${HandIcon})` - } - }, - controls : - { - float : 'right', - display : 'flex', - flexDirection : 'row', - justifyContent : 'flex-start', - alignItems : 'center' + color : 'rgba(0, 153, 0, 1)' } }); @@ -141,105 +95,95 @@ const ListPeer = (props) =>
    { peer.raisedHand && -
    + }
    + { screenConsumer && + + { + e.stopPropagation(); + + screenVisible ? + roomClient.modifyPeerConsumer(peer.id, 'screen', true) : + roomClient.modifyPeerConsumer(peer.id, 'screen', false); + }} + > + { screenVisible ? + + : + + } + + } + + { + e.stopPropagation(); + + webcamEnabled ? + roomClient.modifyPeerConsumer(peer.id, 'webcam', true) : + roomClient.modifyPeerConsumer(peer.id, 'webcam', false); + }} + > + { webcamEnabled ? + + : + + } + + + { + e.stopPropagation(); + + micEnabled ? + roomClient.modifyPeerConsumer(peer.id, 'mic', true) : + roomClient.modifyPeerConsumer(peer.id, 'mic', false); + }} + > + { micEnabled ? + + : + + } + + { isModerator && + + { + e.stopPropagation(); + + roomClient.kickPeer(peer.id); + }} + > + + + } {children} -
    - { screenConsumer && - - { - e.stopPropagation(); - - screenVisible ? - roomClient.modifyPeerConsumer(peer.id, 'screen', true) : - roomClient.modifyPeerConsumer(peer.id, 'screen', false); - }} - > - { screenVisible ? - - : - - } - - } - - { - e.stopPropagation(); - - webcamEnabled ? - roomClient.modifyPeerConsumer(peer.id, 'webcam', true) : - roomClient.modifyPeerConsumer(peer.id, 'webcam', false); - }} - > - { webcamEnabled ? - - : - - } - - - { - e.stopPropagation(); - - micEnabled ? - roomClient.modifyPeerConsumer(peer.id, 'mic', true) : - roomClient.modifyPeerConsumer(peer.id, 'mic', false); - }} - > - { micEnabled ? - - : - - } - - { isModerator && - - { - e.stopPropagation(); - - roomClient.kickPeer(peer.id); - }} - > - - - } -
    ); }; diff --git a/app/src/images/icon-hand-black.svg b/app/src/images/icon-hand-black.svg deleted file mode 100644 index 8f0f065..0000000 --- a/app/src/images/icon-hand-black.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - image/svg+xml - - - diff --git a/app/src/images/icon-hand-white.svg b/app/src/images/icon-hand-white.svg deleted file mode 100644 index 0e2f05f..0000000 --- a/app/src/images/icon-hand-white.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - image/svg+xml - - - diff --git a/app/src/translations/cn.json b/app/src/translations/cn.json index ce6f28b..39c140e 100644 --- a/app/src/translations/cn.json +++ b/app/src/translations/cn.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "房间名称", "label.chooseRoomButton": "继续", diff --git a/app/src/translations/cs.json b/app/src/translations/cs.json index 6b4c339..01bb98f 100644 --- a/app/src/translations/cs.json +++ b/app/src/translations/cs.json @@ -74,6 +74,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Jméno místnosti", "label.chooseRoomButton": "Pokračovat", diff --git a/app/src/translations/de.json b/app/src/translations/de.json index e530c7c..54d7e03 100644 --- a/app/src/translations/de.json +++ b/app/src/translations/de.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": "Teilnehmer rauswerfen", "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Name des Raums", "label.chooseRoomButton": "Weiter", diff --git a/app/src/translations/dk.json b/app/src/translations/dk.json index af8863b..b475816 100644 --- a/app/src/translations/dk.json +++ b/app/src/translations/dk.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Værelsesnavn", "label.chooseRoomButton": "Fortsæt", diff --git a/app/src/translations/el.json b/app/src/translations/el.json index cb3c3f1..8a93b44 100644 --- a/app/src/translations/el.json +++ b/app/src/translations/el.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Όνομα δωματίου", "label.chooseRoomButton": "Συνέχεια", diff --git a/app/src/translations/en.json b/app/src/translations/en.json index 9df2d8c..c2c814f 100644 --- a/app/src/translations/en.json +++ b/app/src/translations/en.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": "Kick out participant", "tooltip.muteParticipant": "Mute participant", "tooltip.muteParticipantVideo": "Mute participant video", + "tooltip.raisedHand": "Raise hand", "label.roomName": "Room name", "label.chooseRoomButton": "Continue", diff --git a/app/src/translations/es.json b/app/src/translations/es.json index 0974d89..e3cd91f 100644 --- a/app/src/translations/es.json +++ b/app/src/translations/es.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Nombre de la sala", "label.chooseRoomButton": "Continuar", diff --git a/app/src/translations/fr.json b/app/src/translations/fr.json index f51959c..f0f4c03 100644 --- a/app/src/translations/fr.json +++ b/app/src/translations/fr.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Nom de la salle", "label.chooseRoomButton": "Continuer", diff --git a/app/src/translations/hr.json b/app/src/translations/hr.json index 0126080..198e7b9 100644 --- a/app/src/translations/hr.json +++ b/app/src/translations/hr.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": "Izbaci sudionika", "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Naziv sobe", "label.chooseRoomButton": "Nastavi", diff --git a/app/src/translations/hu.json b/app/src/translations/hu.json index a02ad2c..6e1b8b2 100644 --- a/app/src/translations/hu.json +++ b/app/src/translations/hu.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Konferencia", "label.chooseRoomButton": "Tovább", diff --git a/app/src/translations/it.json b/app/src/translations/it.json index d9abab0..3554a5f 100644 --- a/app/src/translations/it.json +++ b/app/src/translations/it.json @@ -74,6 +74,7 @@ "tooltip.participants": "Mostra partecipanti", "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Nome della stanza", "label.chooseRoomButton": "Continua", diff --git a/app/src/translations/nb.json b/app/src/translations/nb.json index 146bf9e..d164132 100644 --- a/app/src/translations/nb.json +++ b/app/src/translations/nb.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": "Spark ut deltaker", "tooltip.muteParticipant": "Demp deltaker", "tooltip.muteParticipantVideo": "Demp deltakervideo", + "tooltip.raisedHand": "Rekk opp hånden", "label.roomName": "Møtenavn", "label.chooseRoomButton": "Fortsett", diff --git a/app/src/translations/pl.json b/app/src/translations/pl.json index 97034e1..0f3ff78 100644 --- a/app/src/translations/pl.json +++ b/app/src/translations/pl.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Nazwa konferencji", "label.chooseRoomButton": "Kontynuuj", diff --git a/app/src/translations/pt.json b/app/src/translations/pt.json index 7149910..8ba5f23 100644 --- a/app/src/translations/pt.json +++ b/app/src/translations/pt.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Nome da sala", "label.chooseRoomButton": "Continuar", diff --git a/app/src/translations/ro.json b/app/src/translations/ro.json index 5fce442..d644f14 100644 --- a/app/src/translations/ro.json +++ b/app/src/translations/ro.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Numele camerei", "label.chooseRoomButton": "Continuare", diff --git a/app/src/translations/tr.json b/app/src/translations/tr.json index 835013e..cbca7c8 100644 --- a/app/src/translations/tr.json +++ b/app/src/translations/tr.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Oda adı", "label.chooseRoomButton": "Devam", diff --git a/app/src/translations/uk.json b/app/src/translations/uk.json index f3d6aea..60c569d 100644 --- a/app/src/translations/uk.json +++ b/app/src/translations/uk.json @@ -75,6 +75,7 @@ "tooltip.kickParticipant": null, "tooltip.muteParticipant": null, "tooltip.muteParticipantVideo": null, + "tooltip.raisedHand": null, "label.roomName": "Назва кімнати", "label.chooseRoomButton": "Продовжити", From 130ed19f13ad6e42675e9b289b1459a3bc6e2ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Sat, 2 May 2020 21:57:53 +0200 Subject: [PATCH 15/30] Settings split into tabs. --- app/src/actions/roomActions.js | 8 +- app/src/components/Controls/TopBar.js | 2 +- .../components/Settings/AdvancedSettings.js | 114 +++++ .../components/Settings/AppearenceSettings.js | 132 ++++++ app/src/components/Settings/MediaSettings.js | 284 ++++++++++++ app/src/components/Settings/Settings.js | 432 +++--------------- app/src/reducers/room.js | 8 + app/src/translations/cn.json | 3 + app/src/translations/cs.json | 3 + app/src/translations/de.json | 3 + app/src/translations/dk.json | 3 + app/src/translations/el.json | 3 + app/src/translations/en.json | 3 + app/src/translations/es.json | 3 + app/src/translations/fr.json | 3 + app/src/translations/hr.json | 3 + app/src/translations/hu.json | 3 + app/src/translations/it.json | 3 + app/src/translations/nb.json | 3 + app/src/translations/pl.json | 3 + app/src/translations/pt.json | 3 + app/src/translations/ro.json | 3 + app/src/translations/tr.json | 3 + app/src/translations/uk.json | 3 + 24 files changed, 663 insertions(+), 368 deletions(-) create mode 100644 app/src/components/Settings/AdvancedSettings.js create mode 100644 app/src/components/Settings/AppearenceSettings.js create mode 100644 app/src/components/Settings/MediaSettings.js diff --git a/app/src/actions/roomActions.js b/app/src/actions/roomActions.js index ac97179..ba9d2dc 100644 --- a/app/src/actions/roomActions.js +++ b/app/src/actions/roomActions.js @@ -52,12 +52,18 @@ export const setJoinByAccessCode = (joinByAccessCode) => payload : { joinByAccessCode } }); -export const setSettingsOpen = ({ settingsOpen }) => +export const setSettingsOpen = (settingsOpen) => ({ type : 'SET_SETTINGS_OPEN', payload : { settingsOpen } }); +export const setSettingsTab = (tab) => + ({ + type : 'SET_SETTINGS_TAB', + payload : { tab } + }); + export const setLockDialogOpen = ({ lockDialogOpen }) => ({ type : 'SET_LOCK_DIALOG_OPEN', diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js index 7f4be52..9b7ae92 100644 --- a/app/src/components/Controls/TopBar.js +++ b/app/src/components/Controls/TopBar.js @@ -427,7 +427,7 @@ const mapDispatchToProps = (dispatch) => }, setSettingsOpen : (settingsOpen) => { - dispatch(roomActions.setSettingsOpen({ settingsOpen })); + dispatch(roomActions.setSettingsOpen(settingsOpen)); }, setLockDialogOpen : (lockDialogOpen) => { diff --git a/app/src/components/Settings/AdvancedSettings.js b/app/src/components/Settings/AdvancedSettings.js new file mode 100644 index 0000000..131395e --- /dev/null +++ b/app/src/components/Settings/AdvancedSettings.js @@ -0,0 +1,114 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { withStyles } from '@material-ui/core/styles'; +import { withRoomContext } from '../../RoomContext'; +import * as settingsActions from '../../actions/settingsActions'; +import PropTypes from 'prop-types'; +import { useIntl, FormattedMessage } from 'react-intl'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControl from '@material-ui/core/FormControl'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Select from '@material-ui/core/Select'; +import Checkbox from '@material-ui/core/Checkbox'; + +const styles = (theme) => + ({ + setting : + { + padding : theme.spacing(2) + }, + formControl : + { + display : 'flex' + } + }); + +const AdvancedSettings = ({ + roomClient, + settings, + onToggleAdvancedMode, + classes +}) => +{ + const intl = useIntl(); + + return ( + + } + label={intl.formatMessage({ + id : 'settings.advancedMode', + defaultMessage : 'Advanced mode' + })} + /> + { !window.config.lockLastN && +
    + + + + + + +
    + } +
    + ); +}; + +AdvancedSettings.propTypes = +{ + roomClient : PropTypes.any.isRequired, + settings : PropTypes.object.isRequired, + onToggleAdvancedMode : PropTypes.func.isRequired, + classes : PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => + ({ + settings : state.settings + }); + +const mapDispatchToProps = { + onToggleAdvancedMode : settingsActions.toggleAdvancedMode +}; + +export default withRoomContext(connect( + mapStateToProps, + mapDispatchToProps, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.settings === next.settings + ); + } + } +)(withStyles(styles)(AdvancedSettings))); \ No newline at end of file diff --git a/app/src/components/Settings/AppearenceSettings.js b/app/src/components/Settings/AppearenceSettings.js new file mode 100644 index 0000000..71f250a --- /dev/null +++ b/app/src/components/Settings/AppearenceSettings.js @@ -0,0 +1,132 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import * as appPropTypes from '../appPropTypes'; +import { withStyles } from '@material-ui/core/styles'; +import * as roomActions from '../../actions/roomActions'; +import * as settingsActions from '../../actions/settingsActions'; +import PropTypes from 'prop-types'; +import { useIntl, FormattedMessage } from 'react-intl'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControl from '@material-ui/core/FormControl'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Select from '@material-ui/core/Select'; +import Checkbox from '@material-ui/core/Checkbox'; + +const styles = (theme) => + ({ + setting : + { + padding : theme.spacing(2) + }, + formControl : + { + display : 'flex' + } + }); + +const AppearenceSettings = ({ + room, + settings, + onTogglePermanentTopBar, + handleChangeMode, + classes +}) => +{ + const intl = useIntl(); + + const modes = [ { + value : 'democratic', + label : intl.formatMessage({ + id : 'label.democratic', + defaultMessage : 'Democratic view' + }) + }, { + value : 'filmstrip', + label : intl.formatMessage({ + id : 'label.filmstrip', + defaultMessage : 'Filmstrip view' + }) + } ]; + + return ( + +
    + + + + + + +
    + } + label={intl.formatMessage({ + id : 'settings.permanentTopBar', + defaultMessage : 'Permanent top bar' + })} + /> +
    + ); +}; + +AppearenceSettings.propTypes = +{ + room : appPropTypes.Room.isRequired, + settings : PropTypes.object.isRequired, + onTogglePermanentTopBar : PropTypes.func.isRequired, + handleChangeMode : PropTypes.func.isRequired, + classes : PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => + ({ + room : state.room, + settings : state.settings + }); + +const mapDispatchToProps = { + onTogglePermanentTopBar : settingsActions.togglePermanentTopBar, + handleChangeMode : roomActions.setDisplayMode +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.room === next.room && + prev.settings === next.settings + ); + } + } +)(withStyles(styles)(AppearenceSettings)); \ No newline at end of file diff --git a/app/src/components/Settings/MediaSettings.js b/app/src/components/Settings/MediaSettings.js new file mode 100644 index 0000000..fa9728b --- /dev/null +++ b/app/src/components/Settings/MediaSettings.js @@ -0,0 +1,284 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import * as appPropTypes from '../appPropTypes'; +import { withStyles } from '@material-ui/core/styles'; +import { withRoomContext } from '../../RoomContext'; +import PropTypes from 'prop-types'; +import { useIntl, FormattedMessage } from 'react-intl'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; + +const styles = (theme) => + ({ + setting : + { + padding : theme.spacing(2) + }, + formControl : + { + display : 'flex' + } + }); + +const MediaSettings = ({ + roomClient, + me, + settings, + classes +}) => +{ + const intl = useIntl(); + + const resolutions = [ { + value : 'low', + label : intl.formatMessage({ + id : 'label.low', + defaultMessage : 'Low' + }) + }, + { + value : 'medium', + label : intl.formatMessage({ + id : 'label.medium', + defaultMessage : 'Medium' + }) + }, + { + value : 'high', + label : intl.formatMessage({ + id : 'label.high', + defaultMessage : 'High (HD)' + }) + }, + { + value : 'veryhigh', + label : intl.formatMessage({ + id : 'label.veryHigh', + defaultMessage : 'Very high (FHD)' + }) + }, + { + value : 'ultra', + label : intl.formatMessage({ + id : 'label.ultra', + defaultMessage : 'Ultra (UHD)' + }) + } ]; + + let webcams; + + if (me.webcamDevices) + webcams = Object.values(me.webcamDevices); + else + webcams = []; + + let audioDevices; + + if (me.audioDevices) + audioDevices = Object.values(me.audioDevices); + else + audioDevices = []; + + let audioOutputDevices; + + if (me.audioOutputDevices) + audioOutputDevices = Object.values(me.audioOutputDevices); + else + audioOutputDevices = []; + + return ( + +
    + + + + { webcams.length > 0 ? + intl.formatMessage({ + id : 'settings.selectCamera', + defaultMessage : 'Select video device' + }) + : + intl.formatMessage({ + id : 'settings.cantSelectCamera', + defaultMessage : 'Unable to select video device' + }) + } + + +
    +
    + + + + { audioDevices.length > 0 ? + intl.formatMessage({ + id : 'settings.selectAudio', + defaultMessage : 'Select audio device' + }) + : + intl.formatMessage({ + id : 'settings.cantSelectAudio', + defaultMessage : 'Unable to select audio device' + }) + } + + +
    + { 'audioOutputSupportedBrowsers' in window.config && + window.config.audioOutputSupportedBrowsers.includes(me.browser.name) && +
    + + + + { audioOutputDevices.length > 0 ? + intl.formatMessage({ + id : 'settings.selectAudioOutput', + defaultMessage : 'Select audio output device' + }) + : + intl.formatMessage({ + id : 'settings.cantSelectAudioOutput', + defaultMessage : 'Unable to select audio output device' + }) + } + + +
    + } +
    + + + + + + +
    +
    + ); +}; + +MediaSettings.propTypes = +{ + roomClient : PropTypes.any.isRequired, + me : appPropTypes.Me.isRequired, + settings : PropTypes.object.isRequired, + classes : PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => +{ + return { + me : state.me, + settings : state.settings + }; +}; + +export default withRoomContext(connect( + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.me === next.me && + prev.settings === next.settings + ); + } + } +)(withStyles(styles)(MediaSettings))); \ No newline at end of file diff --git a/app/src/components/Settings/Settings.js b/app/src/components/Settings/Settings.js index 064aa2f..6633829 100644 --- a/app/src/components/Settings/Settings.js +++ b/app/src/components/Settings/Settings.js @@ -1,22 +1,25 @@ import React from 'react'; import { connect } from 'react-redux'; -import * as appPropTypes from '../appPropTypes'; import { withStyles } from '@material-ui/core/styles'; -import { withRoomContext } from '../../RoomContext'; import * as roomActions from '../../actions/roomActions'; -import * as settingsActions from '../../actions/settingsActions'; import PropTypes from 'prop-types'; import { useIntl, FormattedMessage } from 'react-intl'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import MediaSettings from './MediaSettings'; +import AppearenceSettings from './AppearenceSettings'; +import AdvancedSettings from './AdvancedSettings'; import Dialog from '@material-ui/core/Dialog'; import DialogTitle from '@material-ui/core/DialogTitle'; import DialogActions from '@material-ui/core/DialogActions'; import Button from '@material-ui/core/Button'; -import MenuItem from '@material-ui/core/MenuItem'; -import FormHelperText from '@material-ui/core/FormHelperText'; -import FormControl from '@material-ui/core/FormControl'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import Select from '@material-ui/core/Select'; -import Checkbox from '@material-ui/core/Checkbox'; + +const tabs = +[ + 'media', + 'appearence', + 'advanced' +]; const styles = (theme) => ({ @@ -43,106 +46,27 @@ const styles = (theme) => width : '90vw' } }, - setting : + tabsHeader : { - padding : theme.spacing(2) - }, - formControl : - { - display : 'flex' + flexGrow : 1 } }); const Settings = ({ - roomClient, - room, - me, - settings, - onToggleAdvancedMode, - onTogglePermanentTopBar, + currentSettingsTab, + settingsOpen, handleCloseSettings, - handleChangeMode, + setSettingsTab, classes }) => { const intl = useIntl(); - const modes = [ { - value : 'democratic', - label : intl.formatMessage({ - id : 'label.democratic', - defaultMessage : 'Democratic view' - }) - }, { - value : 'filmstrip', - label : intl.formatMessage({ - id : 'label.filmstrip', - defaultMessage : 'Filmstrip view' - }) - } ]; - - const resolutions = [ { - value : 'low', - label : intl.formatMessage({ - id : 'label.low', - defaultMessage : 'Low' - }) - }, - { - value : 'medium', - label : intl.formatMessage({ - id : 'label.medium', - defaultMessage : 'Medium' - }) - }, - { - value : 'high', - label : intl.formatMessage({ - id : 'label.high', - defaultMessage : 'High (HD)' - }) - }, - { - value : 'veryhigh', - label : intl.formatMessage({ - id : 'label.veryHigh', - defaultMessage : 'Very high (FHD)' - }) - }, - { - value : 'ultra', - label : intl.formatMessage({ - id : 'label.ultra', - defaultMessage : 'Ultra (UHD)' - }) - } ]; - - let webcams; - - if (me.webcamDevices) - webcams = Object.values(me.webcamDevices); - else - webcams = []; - - let audioDevices; - - if (me.audioDevices) - audioDevices = Object.values(me.audioDevices); - else - audioDevices = []; - - let audioOutputDevices; - - if (me.audioOutputDevices) - audioOutputDevices = Object.values(me.audioOutputDevices); - else - audioOutputDevices = []; - return ( handleCloseSettings({ settingsOpen: false })} + open={settingsOpen} + onClose={() => handleCloseSettings(false)} classes={{ paper : classes.dialogPaper }} @@ -153,254 +77,40 @@ const Settings = ({ defaultMessage='Settings' /> -
    - - - - { webcams.length > 0 ? - intl.formatMessage({ - id : 'settings.selectCamera', - defaultMessage : 'Select video device' - }) - : - intl.formatMessage({ - id : 'settings.cantSelectCamera', - defaultMessage : 'Unable to select video device' - }) - } - - -
    -
    - - - - { audioDevices.length > 0 ? - intl.formatMessage({ - id : 'settings.selectAudio', - defaultMessage : 'Select audio device' - }) - : - intl.formatMessage({ - id : 'settings.cantSelectAudio', - defaultMessage : 'Unable to select audio device' - }) - } - - -
    - { 'audioOutputSupportedBrowsers' in window.config && - window.config.audioOutputSupportedBrowsers.includes(me.browser.name) && -
    - - - - { audioOutputDevices.length > 0 ? - intl.formatMessage({ - id : 'settings.selectAudioOutput', - defaultMessage : 'Select audio output device' - }) - : - intl.formatMessage({ - id : 'settings.cantSelectAudioOutput', - defaultMessage : 'Unable to select audio output device' - }) - } - - -
    - } -
    - - - - - - -
    -
    - - - - - - -
    - } - label={intl.formatMessage({ - id : 'settings.advancedMode', - defaultMessage : 'Advanced mode' - })} - /> - { settings.advancedMode && - - { !window.config.lockLastN && -
    - - - - - - -
    + setSettingsTab(tabs[value])} + indicatorColor='primary' + textColor='primary' + variant='fullWidth' + > + } - label={intl.formatMessage({ - id : 'settings.permanentTopBar', - defaultMessage : 'Permanent top bar' - })} - /> -
    - } + /> + + + + {currentSettingsTab === 'media' && } + {currentSettingsTab === 'appearence' && } + {currentSettingsTab === 'advanced' && } - - - - - */} { lobbyPeers.length > 0 ? } - + +
    + ); +}; + +ExtraVideo.propTypes = +{ + roomClient : PropTypes.object.isRequired, + extraVideoOpen : PropTypes.bool.isRequired, + webcamDevices : PropTypes.object, + handleCloseExtraVideo : PropTypes.func.isRequired, + classes : PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => + ({ + webcamDevices : state.me.webcamDevices, + extraVideoOpen : state.room.extraVideoOpen + }); + +const mapDispatchToProps = { + handleCloseExtraVideo : roomActions.setExtraVideoOpen +}; + +export default withRoomContext(connect( + mapStateToProps, + mapDispatchToProps, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.me.webcamDevices === next.me.webcamDevices && + prev.room.extraVideoOpen === next.room.extraVideoOpen + ); + } + } +)(withStyles(styles)(ExtraVideo))); \ No newline at end of file diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js index 89bf385..64e56d5 100644 --- a/app/src/components/Controls/TopBar.js +++ b/app/src/components/Controls/TopBar.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { @@ -14,11 +14,14 @@ import * as toolareaActions from '../../actions/toolareaActions'; import { useIntl, FormattedMessage } from 'react-intl'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; +import MenuItem from '@material-ui/core/MenuItem'; +import Menu from '@material-ui/core/Menu'; import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton'; import MenuIcon from '@material-ui/icons/Menu'; import Avatar from '@material-ui/core/Avatar'; import Badge from '@material-ui/core/Badge'; +import ExtensionIcon from '@material-ui/icons/Extension'; import AccountCircle from '@material-ui/icons/AccountCircle'; import FullScreenIcon from '@material-ui/icons/Fullscreen'; import FullScreenExitIcon from '@material-ui/icons/FullscreenExit'; @@ -27,6 +30,7 @@ import SecurityIcon from '@material-ui/icons/Security'; import PeopleIcon from '@material-ui/icons/People'; import LockIcon from '@material-ui/icons/Lock'; import LockOpenIcon from '@material-ui/icons/LockOpen'; +import VideoCallIcon from '@material-ui/icons/VideoCall'; import Button from '@material-ui/core/Button'; import Tooltip from '@material-ui/core/Tooltip'; @@ -89,6 +93,10 @@ const styles = (theme) => green : { color : 'rgba(0, 153, 0, 1)' + }, + moreAction : + { + margin : theme.spacing(0, 0, 0, 1) } }); @@ -127,6 +135,18 @@ const TopBar = (props) => { const intl = useIntl(); + const [ moreActionsElement, setMoreActionsElement ] = useState(null); + + const handleMoreActionsOpen = (event) => + { + setMoreActionsElement(event.currentTarget); + }; + + const handleMoreActionsClose = () => + { + setMoreActionsElement(null); + }; + const { roomClient, room, @@ -140,6 +160,7 @@ const TopBar = (props) => fullscreen, onFullscreen, setSettingsOpen, + setExtraVideoOpen, setLockDialogOpen, toggleToolArea, openUsersTab, @@ -149,6 +170,8 @@ const TopBar = (props) => classes } = props; + const isMoreActionsMenuOpen = Boolean(moreActionsElement); + const lockTooltip = room.locked ? intl.formatMessage({ id : 'tooltip.unLockRoom', @@ -183,196 +206,230 @@ const TopBar = (props) => }); return ( - - - toggleToolArea()} - > - + + + toggleToolArea()} + > + + + + + { window.config.logo && Logo } + - - - - { window.config.logo && Logo } - - { window.config.title ? window.config.title : 'Multiparty meeting' } - -
    -
    - { fullscreenEnabled && - - - { fullscreen ? - - : - - } - - - } - + { window.config.title ? window.config.title : 'Multiparty meeting' } + +
    +
    openUsersTab()} > - - - + - - - setSettingsOpen(!room.settingsOpen)} - > - - - - - - - { - if (room.locked) - { - roomClient.unlockRoom(); - } - else - { - roomClient.lockRoom(); - } - }} - > - { room.locked ? - - : - - } - - - - { lobbyPeers.length > 0 && - - + { fullscreenEnabled && + setLockDialogOpen(!room.lockDialogOpen)} + onClick={onFullscreen} > - - - + { fullscreen ? + + : + + } - - - } - { loginEnabled && - + + } + openUsersTab()} + > + + + + + + + - { - loggedIn ? roomClient.logout() : roomClient.login(); - }} + onClick={() => setSettingsOpen(!room.settingsOpen)} > - { myPicture ? - - : - - } + - } -
    - +
    + + + + + { + handleMoreActionsClose(); + setExtraVideoOpen(!room.extraVideoOpen); + }} + > + roomClient.close()} - > - - -
    - - + /> +

    Add video

    + + + ); }; @@ -391,6 +448,7 @@ TopBar.propTypes = onFullscreen : PropTypes.func.isRequired, setToolbarsVisible : PropTypes.func.isRequired, setSettingsOpen : PropTypes.func.isRequired, + setExtraVideoOpen : PropTypes.func.isRequired, setLockDialogOpen : PropTypes.func.isRequired, toggleToolArea : PropTypes.func.isRequired, openUsersTab : PropTypes.func.isRequired, @@ -430,6 +488,10 @@ const mapDispatchToProps = (dispatch) => { dispatch(roomActions.setSettingsOpen(settingsOpen)); }, + setExtraVideoOpen : (extraVideoOpen) => + { + dispatch(roomActions.setExtraVideoOpen(extraVideoOpen)); + }, setLockDialogOpen : (lockDialogOpen) => { dispatch(roomActions.setLockDialogOpen(lockDialogOpen)); diff --git a/app/src/components/Room.js b/app/src/components/Room.js index fbd4cc5..cdda66f 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -24,6 +24,7 @@ import LockDialog from './AccessControl/LockDialog/LockDialog'; import Settings from './Settings/Settings'; import TopBar from './Controls/TopBar'; import WakeLock from 'react-wakelock-react16'; +import ExtraVideo from './Controls/ExtraVideo'; const TIMEOUT = 5 * 1000; @@ -217,6 +218,10 @@ class Room extends React.PureComponent { room.settingsOpen && } + + { room.extraVideoOpen && + + }
    ); } diff --git a/app/src/components/Selectors.js b/app/src/components/Selectors.js index a33c171..fd22aff 100644 --- a/app/src/components/Selectors.js +++ b/app/src/components/Selectors.js @@ -37,6 +37,11 @@ export const screenProducersSelector = createSelector( (producers) => Object.values(producers).filter((producer) => producer.source === 'screen') ); +export const extraVideoProducersSelector = createSelector( + producersSelect, + (producers) => Object.values(producers).filter((producer) => producer.source === 'extravideo') +); + export const micProducerSelector = createSelector( producersSelect, (producers) => Object.values(producers).find((producer) => producer.source === 'mic') @@ -67,6 +72,24 @@ export const screenConsumerSelector = createSelector( (consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen') ); +export const spotlightScreenConsumerSelector = createSelector( + spotlightsSelector, + consumersSelect, + (spotlights, consumers) => + Object.values(consumers).filter( + (consumer) => consumer.source === 'screen' && spotlights.includes(consumer.peerId) + ) +); + +export const spotlightExtraVideoConsumerSelector = createSelector( + spotlightsSelector, + consumersSelect, + (spotlights, consumers) => + Object.values(consumers).filter( + (consumer) => consumer.source === 'extravideo' && spotlights.includes(consumer.peerId) + ) +); + export const passiveMicConsumerSelector = createSelector( spotlightsSelector, consumersSelect, @@ -114,21 +137,33 @@ export const raisedHandsSelector = createSelector( export const videoBoxesSelector = createSelector( spotlightsLengthSelector, screenProducersSelector, - screenConsumerSelector, - (spotlightsLength, screenProducers, screenConsumers) => - spotlightsLength + 1 + screenProducers.length + screenConsumers.length + spotlightScreenConsumerSelector, + extraVideoProducersSelector, + spotlightExtraVideoConsumerSelector, + ( + spotlightsLength, + screenProducers, + screenConsumers, + extraVideoProducers, + extraVideoConsumers + ) => + spotlightsLength + 1 + screenProducers.length + + screenConsumers.length + extraVideoProducers.length + + extraVideoConsumers.length ); export const meProducersSelector = createSelector( micProducerSelector, webcamProducerSelector, screenProducerSelector, - (micProducer, webcamProducer, screenProducer) => + extraVideoProducersSelector, + (micProducer, webcamProducer, screenProducer, extraVideoProducers) => { return { micProducer, webcamProducer, - screenProducer + screenProducer, + extraVideoProducers }; } ); @@ -151,8 +186,10 @@ export const makePeerConsumerSelector = () => consumersArray.find((consumer) => consumer.source === 'webcam'); const screenConsumer = consumersArray.find((consumer) => consumer.source === 'screen'); + const extraVideoConsumers = + consumersArray.filter((consumer) => consumer.source === 'extravideo'); - return { micConsumer, webcamConsumer, screenConsumer }; + return { micConsumer, webcamConsumer, screenConsumer, extraVideoConsumers }; } ); }; diff --git a/app/src/components/appPropTypes.js b/app/src/components/appPropTypes.js index 0969c7b..0ae4fc2 100644 --- a/app/src/components/appPropTypes.js +++ b/app/src/components/appPropTypes.js @@ -18,9 +18,9 @@ export const Me = PropTypes.shape( export const Producer = PropTypes.shape( { id : PropTypes.string.isRequired, - source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired, + source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired, deviceLabel : PropTypes.string, - type : PropTypes.oneOf([ 'front', 'back', 'screen' ]), + type : PropTypes.oneOf([ 'front', 'back', 'screen', 'extravideo' ]), paused : PropTypes.bool.isRequired, track : PropTypes.any, codec : PropTypes.string.isRequired @@ -37,7 +37,7 @@ export const Consumer = PropTypes.shape( { id : PropTypes.string.isRequired, peerId : PropTypes.string.isRequired, - source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired, + source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired, locallyPaused : PropTypes.bool.isRequired, remotelyPaused : PropTypes.bool.isRequired, profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]), diff --git a/app/src/reducers/room.js b/app/src/reducers/room.js index 08fa664..35c1e12 100644 --- a/app/src/reducers/room.js +++ b/app/src/reducers/room.js @@ -20,6 +20,7 @@ const initialState = selectedPeerId : null, spotlights : [], settingsOpen : false, + extraVideoOpen : false, currentSettingsTab : 'media', // media, appearence, advanced lockDialogOpen : false, joined : false, @@ -114,6 +115,13 @@ const room = (state = initialState, action) => return { ...state, settingsOpen }; } + case 'SET_EXTRA_VIDEO_OPEN': + { + const { extraVideoOpen } = action.payload; + + return { ...state, extraVideoOpen }; + } + case 'SET_SETTINGS_TAB': { const { tab } = action.payload; diff --git a/app/src/translations/cn.json b/app/src/translations/cn.json index 1dd2f75..77de748 100644 --- a/app/src/translations/cn.json +++ b/app/src/translations/cn.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "设置", "settings.camera": "视频设备", diff --git a/app/src/translations/cs.json b/app/src/translations/cs.json index 7cf5ddd..a6a9af4 100644 --- a/app/src/translations/cs.json +++ b/app/src/translations/cs.json @@ -57,6 +57,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -103,6 +104,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Nastavení", "settings.camera": "Kamera", diff --git a/app/src/translations/de.json b/app/src/translations/de.json index 5e69940..9820bcd 100644 --- a/app/src/translations/de.json +++ b/app/src/translations/de.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": "Du bist stummgeschalted, Halte die SPACE-Taste um zu sprechen", @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Einstellungen", "settings.camera": "Kamera", diff --git a/app/src/translations/dk.json b/app/src/translations/dk.json index 096f14b..163b51d 100644 --- a/app/src/translations/dk.json +++ b/app/src/translations/dk.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Indstillinger", "settings.camera": "Kamera", diff --git a/app/src/translations/el.json b/app/src/translations/el.json index c5de505..6b6cdab 100644 --- a/app/src/translations/el.json +++ b/app/src/translations/el.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Ρυθμίσεις", "settings.camera": "Κάμερα", diff --git a/app/src/translations/en.json b/app/src/translations/en.json index 88bb3be..38414b7 100644 --- a/app/src/translations/en.json +++ b/app/src/translations/en.json @@ -58,6 +58,7 @@ "room.moderatoractions": "Moderator actions", "room.raisedHand": "{displayName} raised their hand", "room.loweredHand": "{displayName} put their hand down", + "room.extraVideo": "Extra video", "me.mutedPTT": "You are muted, hold down SPACE-BAR to talk", @@ -104,6 +105,7 @@ "label.media": "Media", "label.appearence": "Appearence", "label.advanced": "Advanced", + "label.addVideo": "Add video", "settings.settings": "Settings", "settings.camera": "Camera", diff --git a/app/src/translations/es.json b/app/src/translations/es.json index 032493d..b149a8a 100644 --- a/app/src/translations/es.json +++ b/app/src/translations/es.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Ajustes", "settings.camera": "Cámara", diff --git a/app/src/translations/fr.json b/app/src/translations/fr.json index 67beebe..6d694e3 100644 --- a/app/src/translations/fr.json +++ b/app/src/translations/fr.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Paramètres", "settings.camera": "Caméra", diff --git a/app/src/translations/hr.json b/app/src/translations/hr.json index 28c5c0f..c047ce4 100644 --- a/app/src/translations/hr.json +++ b/app/src/translations/hr.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": "Utišani ste, pritisnite i držite SPACE tipku za razgovor", @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Postavke", "settings.camera": "Kamera", diff --git a/app/src/translations/hu.json b/app/src/translations/hu.json index e85ffa2..44e9931 100644 --- a/app/src/translations/hu.json +++ b/app/src/translations/hu.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Beállítások", "settings.camera": "Kamera", diff --git a/app/src/translations/it.json b/app/src/translations/it.json index 3166480..302a8fb 100644 --- a/app/src/translations/it.json +++ b/app/src/translations/it.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -103,6 +104,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Impostazioni", "settings.camera": "Videocamera", diff --git a/app/src/translations/nb.json b/app/src/translations/nb.json index fb741b0..375ffae 100644 --- a/app/src/translations/nb.json +++ b/app/src/translations/nb.json @@ -58,6 +58,7 @@ "room.moderatoractions": "Moderatorhandlinger", "room.raisedHand": "{displayName} rakk opp hånden", "room.loweredHand": "{displayName} tok ned hånden", + "room.extraVideo": "Ekstra video", "me.mutedPTT": "Du er dempet, hold nede SPACE for å snakke", @@ -104,6 +105,7 @@ "label.media": "Media", "label.appearence": "Utseende", "label.advanced": "Avansert", + "label.addVideo": "Legg til video", "settings.settings": "Innstillinger", "settings.camera": "Kamera", diff --git a/app/src/translations/pl.json b/app/src/translations/pl.json index dee0dcc..75a6b15 100644 --- a/app/src/translations/pl.json +++ b/app/src/translations/pl.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Ustawienia", "settings.camera": "Kamera", diff --git a/app/src/translations/pt.json b/app/src/translations/pt.json index af6b783..a823925 100644 --- a/app/src/translations/pt.json +++ b/app/src/translations/pt.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Definições", "settings.camera": "Camera", diff --git a/app/src/translations/ro.json b/app/src/translations/ro.json index e37dda3..c63133f 100644 --- a/app/src/translations/ro.json +++ b/app/src/translations/ro.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Setări", "settings.camera": "Cameră video", diff --git a/app/src/translations/tr.json b/app/src/translations/tr.json index 62a22ed..8502bb3 100644 --- a/app/src/translations/tr.json +++ b/app/src/translations/tr.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Ayarlar", "settings.camera": "Kamera", diff --git a/app/src/translations/uk.json b/app/src/translations/uk.json index de5b736..b783913 100644 --- a/app/src/translations/uk.json +++ b/app/src/translations/uk.json @@ -58,6 +58,7 @@ "room.moderatoractions": null, "room.raisedHand": null, "room.loweredHand": null, + "room.extraVideo": null, "me.mutedPTT": null, @@ -104,6 +105,7 @@ "label.media": null, "label.appearence": null, "label.advanced": null, + "label.addVideo": null, "settings.settings": "Налаштування", "settings.camera": "Камера", From 28bad32f6975492a075f87aa2ee39b16daa5238f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Mon, 4 May 2020 00:40:39 +0200 Subject: [PATCH 25/30] Add permission for sending extra video, fixes #280 --- app/src/components/Controls/TopBar.js | 50 +++++++++++++++------------ app/src/reducers/room.js | 1 + server/config/config.example.js | 2 ++ server/lib/Room.js | 8 +++++ 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js index 64e56d5..77970dc 100644 --- a/app/src/components/Controls/TopBar.js +++ b/app/src/components/Controls/TopBar.js @@ -165,6 +165,7 @@ const TopBar = (props) => toggleToolArea, openUsersTab, unread, + canProduceExtraVideo, canLock, canPromote, classes @@ -414,6 +415,7 @@ const TopBar = (props) => > { handleMoreActionsClose(); @@ -435,28 +437,29 @@ const TopBar = (props) => TopBar.propTypes = { - roomClient : PropTypes.object.isRequired, - room : appPropTypes.Room.isRequired, - peersLength : PropTypes.number, - lobbyPeers : PropTypes.array, - permanentTopBar : PropTypes.bool, - myPicture : PropTypes.string, - loggedIn : PropTypes.bool.isRequired, - loginEnabled : PropTypes.bool.isRequired, - fullscreenEnabled : PropTypes.bool, - fullscreen : PropTypes.bool, - onFullscreen : PropTypes.func.isRequired, - setToolbarsVisible : PropTypes.func.isRequired, - setSettingsOpen : PropTypes.func.isRequired, - setExtraVideoOpen : PropTypes.func.isRequired, - setLockDialogOpen : PropTypes.func.isRequired, - toggleToolArea : PropTypes.func.isRequired, - openUsersTab : PropTypes.func.isRequired, - unread : PropTypes.number.isRequired, - canLock : PropTypes.bool.isRequired, - canPromote : PropTypes.bool.isRequired, - classes : PropTypes.object.isRequired, - theme : PropTypes.object.isRequired + roomClient : PropTypes.object.isRequired, + room : appPropTypes.Room.isRequired, + peersLength : PropTypes.number, + lobbyPeers : PropTypes.array, + permanentTopBar : PropTypes.bool, + myPicture : PropTypes.string, + loggedIn : PropTypes.bool.isRequired, + loginEnabled : PropTypes.bool.isRequired, + fullscreenEnabled : PropTypes.bool, + fullscreen : PropTypes.bool, + onFullscreen : PropTypes.func.isRequired, + setToolbarsVisible : PropTypes.func.isRequired, + setSettingsOpen : PropTypes.func.isRequired, + setExtraVideoOpen : PropTypes.func.isRequired, + setLockDialogOpen : PropTypes.func.isRequired, + toggleToolArea : PropTypes.func.isRequired, + openUsersTab : PropTypes.func.isRequired, + unread : PropTypes.number.isRequired, + canProduceExtraVideo : PropTypes.bool.isRequired, + canLock : PropTypes.bool.isRequired, + canPromote : PropTypes.bool.isRequired, + classes : PropTypes.object.isRequired, + theme : PropTypes.object.isRequired }; const mapStateToProps = (state) => @@ -470,6 +473,9 @@ const mapStateToProps = (state) => myPicture : state.me.picture, unread : state.toolarea.unreadMessages + state.toolarea.unreadFiles + raisedHandsSelector(state), + canProduceExtraVideo : + state.me.roles.some((role) => + state.room.permissionsFromRoles.EXTRA_VIDEO.includes(role)), canLock : state.me.roles.some((role) => state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)), diff --git a/app/src/reducers/room.js b/app/src/reducers/room.js index 35c1e12..0e422df 100644 --- a/app/src/reducers/room.js +++ b/app/src/reducers/room.js @@ -36,6 +36,7 @@ const initialState = SEND_CHAT : [], MODERATE_CHAT : [], SHARE_SCREEN : [], + EXTRA_VIDEO : [], SHARE_FILE : [], MODERATE_FILES : [], MODERATE_ROOM : [] diff --git a/server/config/config.example.js b/server/config/config.example.js index a1f9b84..12bf938 100644 --- a/server/config/config.example.js +++ b/server/config/config.example.js @@ -235,6 +235,8 @@ module.exports = MODERATE_CHAT : [ userRoles.MODERATOR ], // The role(s) have permission to share screen SHARE_SCREEN : [ userRoles.NORMAL ], + // The role(s) have permission to produce extra video + EXTRA_VIDEO : [ userRoles.NORMAL ], // The role(s) have permission to share files SHARE_FILE : [ userRoles.NORMAL ], // The role(s) have permission to moderate files diff --git a/server/lib/Room.js b/server/lib/Room.js index fc9b2e9..4250fd5 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -24,6 +24,7 @@ const permissionsFromRoles = SEND_CHAT : [ userRoles.NORMAL ], MODERATE_CHAT : [ userRoles.MODERATOR ], SHARE_SCREEN : [ userRoles.NORMAL ], + EXTRA_VIDEO : [ userRoles.NORMAL ], SHARE_FILE : [ userRoles.NORMAL ], MODERATE_FILES : [ userRoles.MODERATOR ], MODERATE_ROOM : [ userRoles.MODERATOR ], @@ -747,6 +748,13 @@ class Room extends EventEmitter ) throw new Error('peer not authorized'); + if ( + appData.source === 'extravideo' && + !peer.roles.some( + (role) => permissionsFromRoles.EXTRA_VIDEO.includes(role)) + ) + throw new Error('peer not authorized'); + // Ensure the Peer is joined. if (!peer.joined) throw new Error('Peer not yet joined'); From 29f3c5b036393cd51fcb2917eb0c37268bf41345 Mon Sep 17 00:00:00 2001 From: christian2 Date: Mon, 4 May 2020 07:33:24 +0200 Subject: [PATCH 26/30] MIT License --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a47de2..a62aa4e 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ This started as a fork of the [work](https://github.com/versatica/mediasoup-demo ## License -MIT +MIT License (see `LICENSE.md`) Contributions to this work were made on behalf of the GÉANT project, a project that has received funding from the European Union’s Horizon 2020 research and innovation programme under Grant Agreement No. 731122 (GN4-2). On behalf of GÉANT project, GÉANT Association is the sole owner of the copyright in all material which was developed by a member of the GÉANT project. From dd8173081b633a0ea3ee1c57bb89e068f667d88f Mon Sep 17 00:00:00 2001 From: christian-2 <49752982+christian-2@users.noreply.github.com> Date: Mon, 4 May 2020 07:36:29 +0200 Subject: [PATCH 27/30] MIT License --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b3d9d19 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 GÉANT Association + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 5d2634acbeb4e2ca233a65a183599a3988d2aa16 Mon Sep 17 00:00:00 2001 From: christian2 Date: Mon, 4 May 2020 07:49:09 +0200 Subject: [PATCH 28/30] MIT License --- LICENSE => LICENSE.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename LICENSE => LICENSE.md (100%) diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md From 2ed7c5711cb05752ec96582b2d56a2283fc4ea31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Mon, 4 May 2020 08:49:22 +0200 Subject: [PATCH 29/30] Missing translation in extra video menu item --- app/src/components/Controls/TopBar.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js index 77970dc..cbe4bee 100644 --- a/app/src/components/Controls/TopBar.js +++ b/app/src/components/Controls/TopBar.js @@ -424,11 +424,16 @@ const TopBar = (props) => > -

    Add video

    +

    + +

    From e5fcda9fbf35999d9ddd2cf0895c3611ff0b7317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9sz=C3=A1ros=20Mih=C3=A1ly?= Date: Mon, 4 May 2020 08:51:36 +0200 Subject: [PATCH 30/30] Bump version to 3.3.0 for ansible config templates --- app/package.json | 2 +- server/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index 9392a4a..ec92c5b 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "multiparty-meeting", - "version": "3.2.0", + "version": "3.3.0", "private": true, "description": "multiparty meeting service", "author": "Håvar Aambø Fosstveit ", diff --git a/server/package.json b/server/package.json index c1b7ad4..50c5262 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "multiparty-meeting-server", - "version": "3.2.0", + "version": "3.3.0", "private": true, "description": "multiparty meeting server", "author": "Håvar Aambø Fosstveit ",