Merge remote-tracking branch 'upstream/develop' into mm-exporter
commit
85c9062f86
|
|
@ -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.
|
||||||
|
|
@ -134,7 +134,7 @@ This started as a fork of the [work](https://github.com/versatica/mediasoup-demo
|
||||||
|
|
||||||
## License
|
## 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.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "multiparty-meeting",
|
"name": "multiparty-meeting",
|
||||||
"version": "3.2.0",
|
"version": "3.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "multiparty meeting service",
|
"description": "multiparty meeting service",
|
||||||
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,9 @@ export default class RoomClient
|
||||||
// Local webcam mediasoup Producer.
|
// Local webcam mediasoup Producer.
|
||||||
this._webcamProducer = null;
|
this._webcamProducer = null;
|
||||||
|
|
||||||
|
// Extra videos being produced
|
||||||
|
this._extraVideoProducers = new Map();
|
||||||
|
|
||||||
// Map of webcam MediaDeviceInfos indexed by deviceId.
|
// Map of webcam MediaDeviceInfos indexed by deviceId.
|
||||||
// @type {Map<String, MediaDeviceInfos>}
|
// @type {Map<String, MediaDeviceInfos>}
|
||||||
this._webcams = {};
|
this._webcams = {};
|
||||||
|
|
@ -516,16 +519,21 @@ export default class RoomClient
|
||||||
|
|
||||||
_soundNotification()
|
_soundNotification()
|
||||||
{
|
{
|
||||||
const alertPromise = this._soundAlert.play();
|
const { notificationSounds } = store.getState().settings;
|
||||||
|
|
||||||
if (alertPromise !== undefined)
|
if (notificationSounds)
|
||||||
{
|
{
|
||||||
alertPromise
|
const alertPromise = this._soundAlert.play();
|
||||||
.then()
|
|
||||||
.catch((error) =>
|
if (alertPromise !== undefined)
|
||||||
{
|
{
|
||||||
logger.error('_soundAlert.play() | failed: %o', error);
|
alertPromise
|
||||||
});
|
.then()
|
||||||
|
.catch((error) =>
|
||||||
|
{
|
||||||
|
logger.error('_soundAlert.play() | failed: %o', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -844,62 +852,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()
|
async muteMic()
|
||||||
{
|
{
|
||||||
logger.debug('muteMic()');
|
logger.debug('muteMic()');
|
||||||
|
|
@ -1561,30 +1513,30 @@ export default class RoomClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendRaiseHandState(state)
|
async setRaisedHand(raisedHand)
|
||||||
{
|
{
|
||||||
logger.debug('sendRaiseHandState: ', state);
|
logger.debug('setRaisedHand: ', raisedHand);
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
meActions.setMyRaiseHandStateInProgress(true));
|
meActions.setRaisedHandInProgress(true));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await this.sendRequest('raiseHand', { raiseHandState: state });
|
await this.sendRequest('raisedHand', { raisedHand });
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
meActions.setMyRaiseHandState(state));
|
meActions.setRaisedHand(raisedHand));
|
||||||
}
|
}
|
||||||
catch (error)
|
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
|
// We need to refresh the component for it to render changed state
|
||||||
store.dispatch(meActions.setMyRaiseHandState(!state));
|
store.dispatch(meActions.setRaisedHand(!raisedHand));
|
||||||
}
|
}
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
meActions.setMyRaiseHandStateInProgress(false));
|
meActions.setRaisedHandInProgress(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
async setMaxSendingSpatialLayer(spatialLayer)
|
async setMaxSendingSpatialLayer(spatialLayer)
|
||||||
|
|
@ -2053,6 +2005,8 @@ export default class RoomClient
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
roomActions.setToolbarsVisible(true));
|
roomActions.setToolbarsVisible(true));
|
||||||
|
|
||||||
|
this._soundNotification();
|
||||||
|
|
||||||
store.dispatch(requestActions.notify(
|
store.dispatch(requestActions.notify(
|
||||||
{
|
{
|
||||||
text : intl.formatMessage({
|
text : intl.formatMessage({
|
||||||
|
|
@ -2064,6 +2018,43 @@ export default class RoomClient
|
||||||
break;
|
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':
|
case 'lobby:peerClosed':
|
||||||
{
|
{
|
||||||
const { peerId } = notification.data;
|
const { peerId } = notification.data;
|
||||||
|
|
@ -2222,6 +2213,48 @@ export default class RoomClient
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'raisedHand':
|
||||||
|
{
|
||||||
|
const { peerId, raisedHand } = notification.data;
|
||||||
|
|
||||||
|
store.dispatch(peerActions.setPeerRaisedHand(peerId, raisedHand));
|
||||||
|
|
||||||
|
const { displayName } = store.getState().peers[peerId];
|
||||||
|
|
||||||
|
let text;
|
||||||
|
|
||||||
|
if (raisedHand)
|
||||||
|
{
|
||||||
|
text = intl.formatMessage({
|
||||||
|
id : 'room.raisedHand',
|
||||||
|
defaultMessage : '{displayName} raised their hand'
|
||||||
|
}, {
|
||||||
|
displayName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = intl.formatMessage({
|
||||||
|
id : 'room.loweredHand',
|
||||||
|
defaultMessage : '{displayName} put their hand down'
|
||||||
|
}, {
|
||||||
|
displayName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayName)
|
||||||
|
{
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
text
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._soundNotification();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'chatMessage':
|
case 'chatMessage':
|
||||||
{
|
{
|
||||||
const { peerId, chatMessage } = notification.data;
|
const { peerId, chatMessage } = notification.data;
|
||||||
|
|
@ -2318,6 +2351,8 @@ export default class RoomClient
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
peerActions.addPeer({ id, displayName, picture, roles, consumers: [] }));
|
peerActions.addPeer({ id, displayName, picture, roles, consumers: [] }));
|
||||||
|
|
||||||
|
this._soundNotification();
|
||||||
|
|
||||||
store.dispatch(requestActions.notify(
|
store.dispatch(requestActions.notify(
|
||||||
{
|
{
|
||||||
text : intl.formatMessage({
|
text : intl.formatMessage({
|
||||||
|
|
@ -2474,7 +2509,9 @@ export default class RoomClient
|
||||||
{
|
{
|
||||||
text : intl.formatMessage({
|
text : intl.formatMessage({
|
||||||
id : 'roles.gotRole',
|
id : 'roles.gotRole',
|
||||||
defaultMessage : `You got the role: ${role}`
|
defaultMessage : 'You got the role: {role}'
|
||||||
|
}, {
|
||||||
|
role
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -2496,7 +2533,9 @@ export default class RoomClient
|
||||||
{
|
{
|
||||||
text : intl.formatMessage({
|
text : intl.formatMessage({
|
||||||
id : 'roles.lostRole',
|
id : 'roles.lostRole',
|
||||||
defaultMessage : `You lost the role: ${role}`
|
defaultMessage : 'You lost the role: {role}'
|
||||||
|
}, {
|
||||||
|
role
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -2694,7 +2733,13 @@ export default class RoomClient
|
||||||
peers,
|
peers,
|
||||||
tracker,
|
tracker,
|
||||||
permissionsFromRoles,
|
permissionsFromRoles,
|
||||||
userRoles
|
userRoles,
|
||||||
|
chatHistory,
|
||||||
|
fileHistory,
|
||||||
|
lastNHistory,
|
||||||
|
locked,
|
||||||
|
lobbyPeers,
|
||||||
|
accessCode
|
||||||
} = await this.sendRequest(
|
} = await this.sendRequest(
|
||||||
'join',
|
'join',
|
||||||
{
|
{
|
||||||
|
|
@ -2749,6 +2794,38 @@ export default class RoomClient
|
||||||
this.updateSpotlights(spotlights);
|
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.
|
// Don't produce if explicitly requested to not to do it.
|
||||||
if (this._produce)
|
if (this._produce)
|
||||||
{
|
{
|
||||||
|
|
@ -2782,8 +2859,6 @@ export default class RoomClient
|
||||||
// Clean all the existing notifications.
|
// Clean all the existing notifications.
|
||||||
store.dispatch(notificationActions.removeAllNotifications());
|
store.dispatch(notificationActions.removeAllNotifications());
|
||||||
|
|
||||||
this.getServerHistory();
|
|
||||||
|
|
||||||
store.dispatch(requestActions.notify(
|
store.dispatch(requestActions.notify(
|
||||||
{
|
{
|
||||||
text : intl.formatMessage({
|
text : intl.formatMessage({
|
||||||
|
|
@ -2933,6 +3008,159 @@ export default class RoomClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addExtraVideo(videoDeviceId)
|
||||||
|
{
|
||||||
|
logger.debug(
|
||||||
|
'addExtraVideo() [videoDeviceId:"%s"]',
|
||||||
|
videoDeviceId
|
||||||
|
);
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setExtraVideoOpen(false));
|
||||||
|
|
||||||
|
if (!this._mediasoupDevice.canProduce('video'))
|
||||||
|
{
|
||||||
|
logger.error('enableWebcam() | cannot produce video');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let track;
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
meActions.setWebcamInProgress(true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const device = this._webcams[videoDeviceId];
|
||||||
|
const resolution = store.getState().settings.resolution;
|
||||||
|
|
||||||
|
if (!device)
|
||||||
|
throw new Error('no webcam devices');
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
'addExtraVideo() | new selected webcam [device:%o]',
|
||||||
|
device);
|
||||||
|
|
||||||
|
logger.debug('_setWebcamProducer() | calling getUserMedia()');
|
||||||
|
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia(
|
||||||
|
{
|
||||||
|
video :
|
||||||
|
{
|
||||||
|
deviceId : { ideal: videoDeviceId },
|
||||||
|
...VIDEO_CONSTRAINS[resolution]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
track = stream.getVideoTracks()[0];
|
||||||
|
|
||||||
|
let producer;
|
||||||
|
|
||||||
|
if (this._useSimulcast)
|
||||||
|
{
|
||||||
|
// If VP9 is the only available video codec then use SVC.
|
||||||
|
const firstVideoCodec = this._mediasoupDevice
|
||||||
|
.rtpCapabilities
|
||||||
|
.codecs
|
||||||
|
.find((c) => c.kind === 'video');
|
||||||
|
|
||||||
|
let encodings;
|
||||||
|
|
||||||
|
if (firstVideoCodec.mimeType.toLowerCase() === 'video/vp9')
|
||||||
|
encodings = VIDEO_KSVC_ENCODINGS;
|
||||||
|
else if ('simulcastEncodings' in window.config)
|
||||||
|
encodings = window.config.simulcastEncodings;
|
||||||
|
else
|
||||||
|
encodings = VIDEO_SIMULCAST_ENCODINGS;
|
||||||
|
|
||||||
|
producer = await this._sendTransport.produce(
|
||||||
|
{
|
||||||
|
track,
|
||||||
|
encodings,
|
||||||
|
codecOptions :
|
||||||
|
{
|
||||||
|
videoGoogleStartBitrate : 1000
|
||||||
|
},
|
||||||
|
appData :
|
||||||
|
{
|
||||||
|
source : 'extravideo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
producer = await this._sendTransport.produce({
|
||||||
|
track,
|
||||||
|
appData :
|
||||||
|
{
|
||||||
|
source : 'extravideo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._extraVideoProducers.set(producer.id, producer);
|
||||||
|
|
||||||
|
store.dispatch(producerActions.addProducer(
|
||||||
|
{
|
||||||
|
id : producer.id,
|
||||||
|
deviceLabel : device.label,
|
||||||
|
source : 'extravideo',
|
||||||
|
paused : producer.paused,
|
||||||
|
track : producer.track,
|
||||||
|
rtpParameters : producer.rtpParameters,
|
||||||
|
codec : producer.rtpParameters.codecs[0].mimeType.split('/')[1]
|
||||||
|
}));
|
||||||
|
|
||||||
|
// store.dispatch(settingsActions.setSelectedWebcamDevice(deviceId));
|
||||||
|
|
||||||
|
await this._updateWebcams();
|
||||||
|
|
||||||
|
producer.on('transportclose', () =>
|
||||||
|
{
|
||||||
|
this._extraVideoProducers.delete(producer.id);
|
||||||
|
|
||||||
|
producer = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
producer.on('trackended', () =>
|
||||||
|
{
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
type : 'error',
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'devices.cameraDisconnected',
|
||||||
|
defaultMessage : 'Camera disconnected'
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.disableExtraVideo(producer.id)
|
||||||
|
.catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug('addExtraVideo() succeeded');
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('addExtraVideo() failed:%o', error);
|
||||||
|
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
type : 'error',
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'devices.cameraError',
|
||||||
|
defaultMessage : 'An error occurred while accessing your camera'
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (track)
|
||||||
|
track.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
meActions.setWebcamInProgress(false));
|
||||||
|
}
|
||||||
|
|
||||||
async enableMic()
|
async enableMic()
|
||||||
{
|
{
|
||||||
if (this._micProducer)
|
if (this._micProducer)
|
||||||
|
|
@ -3433,6 +3661,37 @@ export default class RoomClient
|
||||||
meActions.setWebcamInProgress(false));
|
meActions.setWebcamInProgress(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async disableExtraVideo(id)
|
||||||
|
{
|
||||||
|
logger.debug('disableExtraVideo()');
|
||||||
|
|
||||||
|
const producer = this._extraVideoProducers.get(id);
|
||||||
|
|
||||||
|
if (!producer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
store.dispatch(meActions.setWebcamInProgress(true));
|
||||||
|
|
||||||
|
producer.close();
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
producerActions.removeProducer(id));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.sendRequest(
|
||||||
|
'closeProducer', { producerId: id });
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('disableWebcam() [error:"%o"]', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._extraVideoProducers.delete(id);
|
||||||
|
|
||||||
|
store.dispatch(meActions.setWebcamInProgress(false));
|
||||||
|
}
|
||||||
|
|
||||||
async disableWebcam()
|
async disableWebcam()
|
||||||
{
|
{
|
||||||
logger.debug('disableWebcam()');
|
logger.debug('disableWebcam()');
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ beforeEach(() =>
|
||||||
loggedIn : false,
|
loggedIn : false,
|
||||||
loginEnabled : true,
|
loginEnabled : true,
|
||||||
picture : null,
|
picture : null,
|
||||||
raiseHand : false,
|
raisedHand : false,
|
||||||
raiseHandInProgress : false,
|
raisedHandInProgress : false,
|
||||||
screenShareInProgress : false,
|
screenShareInProgress : false,
|
||||||
webcamDevices : null,
|
webcamDevices : null,
|
||||||
webcamInProgress : false
|
webcamInProgress : false
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,9 @@ export const setWebcamDevices = (devices) =>
|
||||||
payload : { devices }
|
payload : { devices }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setMyRaiseHandState = (flag) =>
|
export const setRaisedHand = (flag) =>
|
||||||
({
|
({
|
||||||
type : 'SET_MY_RAISE_HAND_STATE',
|
type : 'SET_RAISED_HAND',
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -93,9 +93,9 @@ export const setScreenShareInProgress = (flag) =>
|
||||||
payload : { 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 }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ export const setPeerScreenInProgress = (peerId, flag) =>
|
||||||
payload : { peerId, flag }
|
payload : { peerId, flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPeerRaiseHandState = (peerId, raiseHandState) =>
|
export const setPeerRaisedHand = (peerId, raisedHand) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PEER_RAISE_HAND_STATE',
|
type : 'SET_PEER_RAISED_HAND',
|
||||||
payload : { peerId, raiseHandState }
|
payload : { peerId, raisedHand }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPeerPicture = (peerId, picture) =>
|
export const setPeerPicture = (peerId, picture) =>
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,25 @@ export const setJoinByAccessCode = (joinByAccessCode) =>
|
||||||
payload : { joinByAccessCode }
|
payload : { joinByAccessCode }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setSettingsOpen = ({ settingsOpen }) =>
|
export const setSettingsOpen = (settingsOpen) =>
|
||||||
({
|
({
|
||||||
type : 'SET_SETTINGS_OPEN',
|
type : 'SET_SETTINGS_OPEN',
|
||||||
payload : { settingsOpen }
|
payload : { settingsOpen }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setLockDialogOpen = ({ lockDialogOpen }) =>
|
export const setExtraVideoOpen = (extraVideoOpen) =>
|
||||||
|
({
|
||||||
|
type : 'SET_EXTRA_VIDEO_OPEN',
|
||||||
|
payload : { extraVideoOpen }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setSettingsTab = (tab) =>
|
||||||
|
({
|
||||||
|
type : 'SET_SETTINGS_TAB',
|
||||||
|
payload : { tab }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setLockDialogOpen = (lockDialogOpen) =>
|
||||||
({
|
({
|
||||||
type : 'SET_LOCK_DIALOG_OPEN',
|
type : 'SET_LOCK_DIALOG_OPEN',
|
||||||
payload : { lockDialogOpen }
|
payload : { lockDialogOpen }
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,16 @@ export const togglePermanentTopBar = () =>
|
||||||
type : 'TOGGLE_PERMANENT_TOPBAR'
|
type : 'TOGGLE_PERMANENT_TOPBAR'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const toggleHiddenControls = () =>
|
||||||
|
({
|
||||||
|
type : 'TOGGLE_HIDDEN_CONTROLS'
|
||||||
|
});
|
||||||
|
|
||||||
|
export const toggleNotificationSounds = () =>
|
||||||
|
({
|
||||||
|
type : 'TOGGLE_NOTIFICATION_SOUNDS'
|
||||||
|
});
|
||||||
|
|
||||||
export const setLastN = (lastN) =>
|
export const setLastN = (lastN) =>
|
||||||
({
|
({
|
||||||
type : 'SET_LAST_N',
|
type : 'SET_LAST_N',
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,6 @@ import DialogActions from '@material-ui/core/DialogActions';
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent';
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
// import FormLabel from '@material-ui/core/FormLabel';
|
|
||||||
// import FormControl from '@material-ui/core/FormControl';
|
|
||||||
// import FormGroup from '@material-ui/core/FormGroup';
|
|
||||||
// import FormControlLabel from '@material-ui/core/FormControlLabel';
|
|
||||||
// import Checkbox from '@material-ui/core/Checkbox';
|
|
||||||
// import InputLabel from '@material-ui/core/InputLabel';
|
|
||||||
// import OutlinedInput from '@material-ui/core/OutlinedInput';
|
|
||||||
// import Switch from '@material-ui/core/Switch';
|
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||||
import ListLobbyPeer from './ListLobbyPeer';
|
import ListLobbyPeer from './ListLobbyPeer';
|
||||||
|
|
@ -59,10 +51,8 @@ const styles = (theme) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
const LockDialog = ({
|
const LockDialog = ({
|
||||||
// roomClient,
|
|
||||||
room,
|
room,
|
||||||
handleCloseLockDialog,
|
handleCloseLockDialog,
|
||||||
// handleAccessCode,
|
|
||||||
lobbyPeers,
|
lobbyPeers,
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
|
|
@ -71,7 +61,7 @@ const LockDialog = ({
|
||||||
<Dialog
|
<Dialog
|
||||||
className={classes.root}
|
className={classes.root}
|
||||||
open={room.lockDialogOpen}
|
open={room.lockDialogOpen}
|
||||||
onClose={() => handleCloseLockDialog({ lockDialogOpen: false })}
|
onClose={() => handleCloseLockDialog(false)}
|
||||||
classes={{
|
classes={{
|
||||||
paper : classes.dialogPaper
|
paper : classes.dialogPaper
|
||||||
}}
|
}}
|
||||||
|
|
@ -82,54 +72,6 @@ const LockDialog = ({
|
||||||
defaultMessage='Lobby administration'
|
defaultMessage='Lobby administration'
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
{/*
|
|
||||||
<FormControl component='fieldset' className={classes.formControl}>
|
|
||||||
<FormLabel component='legend'>Room lock</FormLabel>
|
|
||||||
<FormGroup>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch checked={room.locked} onChange={() =>
|
|
||||||
{
|
|
||||||
if (room.locked)
|
|
||||||
{
|
|
||||||
roomClient.unlockRoom();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
roomClient.lockRoom();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>}
|
|
||||||
label='Lock'
|
|
||||||
/>
|
|
||||||
TODO: access code
|
|
||||||
<FormControlLabel disabled={ room.locked ? false : true }
|
|
||||||
control={
|
|
||||||
<Checkbox checked={room.joinByAccessCode}
|
|
||||||
onChange={
|
|
||||||
(event) => roomClient.setJoinByAccessCode(event.target.checked)
|
|
||||||
}
|
|
||||||
/>}
|
|
||||||
label='Join by Access code'
|
|
||||||
/>
|
|
||||||
<InputLabel htmlFor='access-code-input' />
|
|
||||||
<OutlinedInput
|
|
||||||
disabled={ room.locked ? false : true }
|
|
||||||
id='acces-code-input'
|
|
||||||
label='Access code'
|
|
||||||
labelWidth={0}
|
|
||||||
variant='outlined'
|
|
||||||
value={room.accessCode}
|
|
||||||
onChange={(event) => handleAccessCode(event.target.value)}
|
|
||||||
>
|
|
||||||
</OutlinedInput>
|
|
||||||
<Button onClick={() => roomClient.setAccessCode(room.accessCode)} color='primary'>
|
|
||||||
save
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</FormGroup>
|
|
||||||
</FormControl>
|
|
||||||
*/}
|
|
||||||
{ lobbyPeers.length > 0 ?
|
{ lobbyPeers.length > 0 ?
|
||||||
<List
|
<List
|
||||||
dense
|
dense
|
||||||
|
|
@ -160,7 +102,7 @@ const LockDialog = ({
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
}
|
}
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => handleCloseLockDialog({ lockDialogOpen: false })} color='primary'>
|
<Button onClick={() => handleCloseLockDialog(false)} color='primary'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='label.close'
|
id='label.close'
|
||||||
defaultMessage='Close'
|
defaultMessage='Close'
|
||||||
|
|
@ -173,7 +115,6 @@ const LockDialog = ({
|
||||||
|
|
||||||
LockDialog.propTypes =
|
LockDialog.propTypes =
|
||||||
{
|
{
|
||||||
// roomClient : PropTypes.any.isRequired,
|
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
handleCloseLockDialog : PropTypes.func.isRequired,
|
handleCloseLockDialog : PropTypes.func.isRequired,
|
||||||
handleAccessCode : PropTypes.func.isRequired,
|
handleAccessCode : PropTypes.func.isRequired,
|
||||||
|
|
@ -202,12 +143,7 @@ export default withRoomContext(connect(
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.locked === next.room.locked &&
|
prev.room === next.room &&
|
||||||
prev.room.joinByAccessCode === next.room.joinByAccessCode &&
|
|
||||||
prev.room.accessCode === next.room.accessCode &&
|
|
||||||
prev.room.code === next.room.code &&
|
|
||||||
prev.room.lockDialogOpen === next.room.lockDialogOpen &&
|
|
||||||
prev.room.codeHidden === next.room.codeHidden &&
|
|
||||||
prev.lobbyPeers === next.lobbyPeers
|
prev.lobbyPeers === next.lobbyPeers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,16 @@ const styles = (theme) =>
|
||||||
zIndex : 21,
|
zIndex : 21,
|
||||||
touchAction : 'none',
|
touchAction : 'none',
|
||||||
pointerEvents : 'none',
|
pointerEvents : 'none',
|
||||||
'& p' :
|
'&.hide' :
|
||||||
|
{
|
||||||
|
transition : 'opacity 0.1s ease-in-out',
|
||||||
|
opacity : 0
|
||||||
|
},
|
||||||
|
'&.hover' :
|
||||||
|
{
|
||||||
|
opacity : 1
|
||||||
|
},
|
||||||
|
'& p' :
|
||||||
{
|
{
|
||||||
position : 'absolute',
|
position : 'absolute',
|
||||||
float : 'left',
|
float : 'left',
|
||||||
|
|
@ -107,7 +116,8 @@ const styles = (theme) =>
|
||||||
fontSize : '2vs',
|
fontSize : '2vs',
|
||||||
backgroundColor : 'rgba(255, 0, 0, 0.5)',
|
backgroundColor : 'rgba(255, 0, 0, 0.5)',
|
||||||
margin : '4px',
|
margin : '4px',
|
||||||
padding : '15px',
|
padding : theme.spacing(2),
|
||||||
|
zIndex : 31,
|
||||||
borderRadius : '20px',
|
borderRadius : '20px',
|
||||||
textAlign : 'center',
|
textAlign : 'center',
|
||||||
opacity : 0,
|
opacity : 0,
|
||||||
|
|
@ -140,6 +150,7 @@ const Me = (props) =>
|
||||||
micProducer,
|
micProducer,
|
||||||
webcamProducer,
|
webcamProducer,
|
||||||
screenProducer,
|
screenProducer,
|
||||||
|
extraVideoProducers,
|
||||||
canShareScreen,
|
canShareScreen,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
@ -289,8 +300,22 @@ const Me = (props) =>
|
||||||
style={spacingStyle}
|
style={spacingStyle}
|
||||||
>
|
>
|
||||||
<div className={classes.viewContainer} style={style}>
|
<div className={classes.viewContainer} style={style}>
|
||||||
|
<div className={classnames(
|
||||||
|
classes.ptt,
|
||||||
|
(micState === 'muted' && me.isSpeaking) ? 'enabled' : null
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='me.mutedPTT'
|
||||||
|
defaultMessage='You are muted, hold down SPACE-BAR to talk'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={classes.controls}
|
className={classnames(
|
||||||
|
classes.controls,
|
||||||
|
settings.hiddenControls ? 'hide' : null,
|
||||||
|
hover ? 'hover' : null
|
||||||
|
)}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -318,17 +343,6 @@ const Me = (props) =>
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className={classnames(
|
|
||||||
classes.ptt,
|
|
||||||
(micState === 'muted' && me.isSpeaking) ? 'enabled' : null
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='me.mutedPTT'
|
|
||||||
defaultMessage='You are muted, hold down SPACE-BAR to talk'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Tooltip title={micTip} placement='left'>
|
<Tooltip title={micTip} placement='left'>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -454,6 +468,112 @@ const Me = (props) =>
|
||||||
</VideoView>
|
</VideoView>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{ extraVideoProducers.map((producer) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<div key={producer.id}
|
||||||
|
className={
|
||||||
|
classnames(
|
||||||
|
classes.root,
|
||||||
|
'webcam',
|
||||||
|
hover ? 'hover' : null,
|
||||||
|
activeSpeaker ? 'active-speaker' : null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onTouchStart={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
setHover(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
touchTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setHover(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
style={spacingStyle}
|
||||||
|
>
|
||||||
|
<div className={classes.viewContainer} style={style}>
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
classes.controls,
|
||||||
|
settings.hiddenControls ? 'hide' : null,
|
||||||
|
hover ? 'hover' : null
|
||||||
|
)}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onTouchStart={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
setHover(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
touchTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setHover(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className={hover ? 'hover' : null}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.me'
|
||||||
|
defaultMessage='ME'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Tooltip title={webcamTip} placement='left'>
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'device.stopVideo',
|
||||||
|
defaultMessage : 'Stop video'
|
||||||
|
})}
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={!me.canSendWebcam || me.webcamInProgress}
|
||||||
|
size={smallButtons ? 'small' : 'large'}
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
roomClient.disableExtraVideo(producer.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VideoIcon />
|
||||||
|
</Fab>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VideoView
|
||||||
|
isMe
|
||||||
|
advancedMode={advancedMode}
|
||||||
|
peer={me}
|
||||||
|
displayName={settings.displayName}
|
||||||
|
showPeerInfo
|
||||||
|
videoTrack={producer && producer.track}
|
||||||
|
videoVisible={videoVisible}
|
||||||
|
videoCodec={producer && producer.codec}
|
||||||
|
onChangeDisplayName={(displayName) =>
|
||||||
|
{
|
||||||
|
roomClient.changeDisplayName(displayName);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{ screenProducer &&
|
{ screenProducer &&
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
|
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
|
||||||
|
|
@ -480,7 +600,11 @@ const Me = (props) =>
|
||||||
>
|
>
|
||||||
<div className={classes.viewContainer} style={style}>
|
<div className={classes.viewContainer} style={style}>
|
||||||
<div
|
<div
|
||||||
className={classes.controls}
|
className={classnames(
|
||||||
|
classes.controls,
|
||||||
|
settings.hiddenControls ? 'hide' : null,
|
||||||
|
hover ? 'hover' : null
|
||||||
|
)}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -528,20 +652,21 @@ const Me = (props) =>
|
||||||
|
|
||||||
Me.propTypes =
|
Me.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
me : appPropTypes.Me.isRequired,
|
me : appPropTypes.Me.isRequired,
|
||||||
settings : PropTypes.object,
|
settings : PropTypes.object,
|
||||||
activeSpeaker : PropTypes.bool,
|
activeSpeaker : PropTypes.bool,
|
||||||
micProducer : appPropTypes.Producer,
|
micProducer : appPropTypes.Producer,
|
||||||
webcamProducer : appPropTypes.Producer,
|
webcamProducer : appPropTypes.Producer,
|
||||||
screenProducer : appPropTypes.Producer,
|
screenProducer : appPropTypes.Producer,
|
||||||
spacing : PropTypes.number,
|
extraVideoProducers : PropTypes.arrayOf(appPropTypes.Producer),
|
||||||
style : PropTypes.object,
|
spacing : PropTypes.number,
|
||||||
smallButtons : PropTypes.bool,
|
style : PropTypes.object,
|
||||||
canShareScreen : PropTypes.bool.isRequired,
|
smallButtons : PropTypes.bool,
|
||||||
classes : PropTypes.object.isRequired,
|
canShareScreen : PropTypes.bool.isRequired,
|
||||||
theme : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired,
|
||||||
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ const Peer = (props) =>
|
||||||
micConsumer,
|
micConsumer,
|
||||||
webcamConsumer,
|
webcamConsumer,
|
||||||
screenConsumer,
|
screenConsumer,
|
||||||
|
extraVideoConsumers,
|
||||||
toggleConsumerFullscreen,
|
toggleConsumerFullscreen,
|
||||||
toggleConsumerWindow,
|
toggleConsumerWindow,
|
||||||
spacing,
|
spacing,
|
||||||
|
|
@ -351,6 +352,161 @@ const Peer = (props) =>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{ extraVideoConsumers.map((consumer) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<div key={consumer.id}
|
||||||
|
className={
|
||||||
|
classnames(
|
||||||
|
classes.root,
|
||||||
|
'webcam',
|
||||||
|
hover ? 'hover' : null,
|
||||||
|
activeSpeaker ? 'active-speaker' : null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onTouchStart={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
setHover(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
touchTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setHover(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
style={rootStyle}
|
||||||
|
>
|
||||||
|
<div className={classnames(classes.viewContainer)}>
|
||||||
|
{ !videoVisible &&
|
||||||
|
<div className={classes.videoInfo}>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.videoPaused'
|
||||||
|
defaultMessage='This video is paused'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={classnames(classes.controls, hover ? 'hover' : null)}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onTouchStart={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
setHover(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
touchTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setHover(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ browser.platform !== 'mobile' &&
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id : 'label.newWindow',
|
||||||
|
defaultMessage : 'New window'
|
||||||
|
})}
|
||||||
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'label.newWindow',
|
||||||
|
defaultMessage : 'New window'
|
||||||
|
})}
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={
|
||||||
|
!videoVisible ||
|
||||||
|
(windowConsumer === consumer.id)
|
||||||
|
}
|
||||||
|
size={smallButtons ? 'small' : 'large'}
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
toggleConsumerWindow(consumer);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewWindowIcon />
|
||||||
|
</Fab>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id : 'label.fullscreen',
|
||||||
|
defaultMessage : 'Fullscreen'
|
||||||
|
})}
|
||||||
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'label.fullscreen',
|
||||||
|
defaultMessage : 'Fullscreen'
|
||||||
|
})}
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={!videoVisible}
|
||||||
|
size={smallButtons ? 'small' : 'large'}
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
toggleConsumerFullscreen(consumer);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FullScreenIcon />
|
||||||
|
</Fab>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VideoView
|
||||||
|
advancedMode={advancedMode}
|
||||||
|
peer={peer}
|
||||||
|
displayName={peer.displayName}
|
||||||
|
showPeerInfo
|
||||||
|
consumerSpatialLayers={consumer ? consumer.spatialLayers : null}
|
||||||
|
consumerTemporalLayers={consumer ? consumer.temporalLayers : null}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
consumer ? consumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
consumer ? consumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
consumer ? consumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
consumer ? consumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={consumer && consumer.type !== 'simple'}
|
||||||
|
videoTrack={consumer && consumer.track}
|
||||||
|
videoVisible={videoVisible}
|
||||||
|
videoCodec={consumer && consumer.codec}
|
||||||
|
videoScore={consumer ? consumer.score : null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
{ screenConsumer &&
|
{ screenConsumer &&
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
|
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
|
||||||
|
|
@ -508,6 +664,7 @@ Peer.propTypes =
|
||||||
micConsumer : appPropTypes.Consumer,
|
micConsumer : appPropTypes.Consumer,
|
||||||
webcamConsumer : appPropTypes.Consumer,
|
webcamConsumer : appPropTypes.Consumer,
|
||||||
screenConsumer : appPropTypes.Consumer,
|
screenConsumer : appPropTypes.Consumer,
|
||||||
|
extraVideoConsumers : PropTypes.arrayOf(appPropTypes.Consumer),
|
||||||
windowConsumer : PropTypes.string,
|
windowConsumer : PropTypes.string,
|
||||||
activeSpeaker : PropTypes.bool,
|
activeSpeaker : PropTypes.bool,
|
||||||
browser : PropTypes.object.isRequired,
|
browser : PropTypes.object.isRequired,
|
||||||
|
|
|
||||||
|
|
@ -91,16 +91,6 @@ const SpeakerPeer = (props) =>
|
||||||
!screenConsumer.remotelyPaused
|
!screenConsumer.remotelyPaused
|
||||||
);
|
);
|
||||||
|
|
||||||
let videoProfile;
|
|
||||||
|
|
||||||
if (webcamConsumer)
|
|
||||||
videoProfile = webcamConsumer.profile;
|
|
||||||
|
|
||||||
let screenProfile;
|
|
||||||
|
|
||||||
if (screenConsumer)
|
|
||||||
screenProfile = screenConsumer.profile;
|
|
||||||
|
|
||||||
const spacingStyle =
|
const spacingStyle =
|
||||||
{
|
{
|
||||||
'margin' : spacing
|
'margin' : spacing
|
||||||
|
|
@ -134,11 +124,27 @@ const SpeakerPeer = (props) =>
|
||||||
peer={peer}
|
peer={peer}
|
||||||
displayName={peer.displayName}
|
displayName={peer.displayName}
|
||||||
showPeerInfo
|
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}
|
videoVisible={videoVisible}
|
||||||
videoProfile={videoProfile}
|
audioCodec={micConsumer && micConsumer.codec}
|
||||||
audioCodec={micConsumer ? micConsumer.codec : null}
|
videoCodec={webcamConsumer && webcamConsumer.codec}
|
||||||
videoCodec={webcamConsumer ? webcamConsumer.codec : null}
|
audioScore={micConsumer ? micConsumer.score : null}
|
||||||
|
videoScore={webcamConsumer ? webcamConsumer.score : null}
|
||||||
>
|
>
|
||||||
<Volume id={peer.id} />
|
<Volume id={peer.id} />
|
||||||
</VideoView>
|
</VideoView>
|
||||||
|
|
@ -165,10 +171,29 @@ const SpeakerPeer = (props) =>
|
||||||
<VideoView
|
<VideoView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoContain
|
videoContain
|
||||||
videoTrack={screenConsumer ? screenConsumer.track : null}
|
consumerSpatialLayers={
|
||||||
|
screenConsumer ? screenConsumer.spatialLayers : null
|
||||||
|
}
|
||||||
|
consumerTemporalLayers={
|
||||||
|
screenConsumer ? screenConsumer.temporalLayers : null
|
||||||
|
}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
screenConsumer ? screenConsumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
screenConsumer ? screenConsumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
screenConsumer ? screenConsumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
screenConsumer ? screenConsumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={screenConsumer && screenConsumer.type !== 'simple'}
|
||||||
|
videoTrack={screenConsumer && screenConsumer.track}
|
||||||
videoVisible={screenVisible}
|
videoVisible={screenVisible}
|
||||||
videoProfile={screenProfile}
|
videoCodec={screenConsumer && screenConsumer.codec}
|
||||||
videoCodec={screenConsumer ? screenConsumer.codec : null}
|
videoScore={screenConsumer ? screenConsumer.score : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import { withRoomContext } from '../../RoomContext';
|
||||||
|
import * as roomActions from '../../actions/roomActions';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
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 Select from '@material-ui/core/Select';
|
||||||
|
|
||||||
|
const styles = (theme) =>
|
||||||
|
({
|
||||||
|
dialogPaper :
|
||||||
|
{
|
||||||
|
width : '30vw',
|
||||||
|
[theme.breakpoints.down('lg')] :
|
||||||
|
{
|
||||||
|
width : '40vw'
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('md')] :
|
||||||
|
{
|
||||||
|
width : '50vw'
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('sm')] :
|
||||||
|
{
|
||||||
|
width : '70vw'
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('xs')] :
|
||||||
|
{
|
||||||
|
width : '90vw'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setting :
|
||||||
|
{
|
||||||
|
padding : theme.spacing(2)
|
||||||
|
},
|
||||||
|
formControl :
|
||||||
|
{
|
||||||
|
display : 'flex'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ExtraVideo = ({
|
||||||
|
roomClient,
|
||||||
|
extraVideoOpen,
|
||||||
|
webcamDevices,
|
||||||
|
handleCloseExtraVideo,
|
||||||
|
classes
|
||||||
|
}) =>
|
||||||
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const [ videoDevice, setVideoDevice ] = React.useState('');
|
||||||
|
|
||||||
|
const handleChange = (event) =>
|
||||||
|
{
|
||||||
|
setVideoDevice(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
let videoDevices;
|
||||||
|
|
||||||
|
if (webcamDevices)
|
||||||
|
videoDevices = Object.values(webcamDevices);
|
||||||
|
else
|
||||||
|
videoDevices = [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={extraVideoOpen}
|
||||||
|
onClose={() => handleCloseExtraVideo(false)}
|
||||||
|
classes={{
|
||||||
|
paper : classes.dialogPaper
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle id='form-dialog-title'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.extraVideo'
|
||||||
|
defaultMessage='Extra video'
|
||||||
|
/>
|
||||||
|
</DialogTitle>
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={videoDevice}
|
||||||
|
displayEmpty
|
||||||
|
name={intl.formatMessage({
|
||||||
|
id : 'settings.camera',
|
||||||
|
defaultMessage : 'Camera'
|
||||||
|
})}
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
disabled={videoDevices.length === 0}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{ videoDevices.map((webcam, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{ videoDevices.length > 0 ?
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.selectCamera',
|
||||||
|
defaultMessage : 'Select video device'
|
||||||
|
})
|
||||||
|
:
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.cantSelectCamera',
|
||||||
|
defaultMessage : 'Unable to select video device'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => roomClient.addExtraVideo(videoDevice)} color='primary'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='label.addVideo'
|
||||||
|
defaultMessage='Add video'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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)));
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
lobbyPeersKeySelector,
|
lobbyPeersKeySelector,
|
||||||
peersLengthSelector
|
peersLengthSelector,
|
||||||
|
raisedHandsSelector
|
||||||
} from '../Selectors';
|
} from '../Selectors';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
import { withRoomContext } from '../../RoomContext';
|
||||||
|
|
@ -13,11 +14,14 @@ import * as toolareaActions from '../../actions/toolareaActions';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
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 Typography from '@material-ui/core/Typography';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import MenuIcon from '@material-ui/icons/Menu';
|
import MenuIcon from '@material-ui/icons/Menu';
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
import Badge from '@material-ui/core/Badge';
|
import Badge from '@material-ui/core/Badge';
|
||||||
|
import ExtensionIcon from '@material-ui/icons/Extension';
|
||||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||||
import FullScreenIcon from '@material-ui/icons/Fullscreen';
|
import FullScreenIcon from '@material-ui/icons/Fullscreen';
|
||||||
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
||||||
|
|
@ -26,6 +30,7 @@ import SecurityIcon from '@material-ui/icons/Security';
|
||||||
import PeopleIcon from '@material-ui/icons/People';
|
import PeopleIcon from '@material-ui/icons/People';
|
||||||
import LockIcon from '@material-ui/icons/Lock';
|
import LockIcon from '@material-ui/icons/Lock';
|
||||||
import LockOpenIcon from '@material-ui/icons/LockOpen';
|
import LockOpenIcon from '@material-ui/icons/LockOpen';
|
||||||
|
import VideoCallIcon from '@material-ui/icons/VideoCall';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
|
||||||
|
|
@ -81,9 +86,17 @@ const styles = (theme) =>
|
||||||
margin : theme.spacing(1, 0),
|
margin : theme.spacing(1, 0),
|
||||||
padding : theme.spacing(0, 1)
|
padding : theme.spacing(0, 1)
|
||||||
},
|
},
|
||||||
|
disabledButton :
|
||||||
|
{
|
||||||
|
margin : theme.spacing(1, 0)
|
||||||
|
},
|
||||||
green :
|
green :
|
||||||
{
|
{
|
||||||
color : 'rgba(0, 153, 0, 1)'
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
|
},
|
||||||
|
moreAction :
|
||||||
|
{
|
||||||
|
margin : theme.spacing(0, 0, 0, 1)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -122,6 +135,18 @@ const TopBar = (props) =>
|
||||||
{
|
{
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const [ moreActionsElement, setMoreActionsElement ] = useState(null);
|
||||||
|
|
||||||
|
const handleMoreActionsOpen = (event) =>
|
||||||
|
{
|
||||||
|
setMoreActionsElement(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMoreActionsClose = () =>
|
||||||
|
{
|
||||||
|
setMoreActionsElement(null);
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
room,
|
room,
|
||||||
|
|
@ -135,15 +160,19 @@ const TopBar = (props) =>
|
||||||
fullscreen,
|
fullscreen,
|
||||||
onFullscreen,
|
onFullscreen,
|
||||||
setSettingsOpen,
|
setSettingsOpen,
|
||||||
|
setExtraVideoOpen,
|
||||||
setLockDialogOpen,
|
setLockDialogOpen,
|
||||||
toggleToolArea,
|
toggleToolArea,
|
||||||
openUsersTab,
|
openUsersTab,
|
||||||
unread,
|
unread,
|
||||||
|
canProduceExtraVideo,
|
||||||
canLock,
|
canLock,
|
||||||
canPromote,
|
canPromote,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const isMoreActionsMenuOpen = Boolean(moreActionsElement);
|
||||||
|
|
||||||
const lockTooltip = room.locked ?
|
const lockTooltip = room.locked ?
|
||||||
intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'tooltip.unLockRoom',
|
id : 'tooltip.unLockRoom',
|
||||||
|
|
@ -178,217 +207,264 @@ const TopBar = (props) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar
|
<React.Fragment>
|
||||||
position='fixed'
|
<AppBar
|
||||||
className={room.toolbarsVisible || permanentTopBar ? classes.show : classes.hide}
|
position='fixed'
|
||||||
>
|
className={room.toolbarsVisible || permanentTopBar ? classes.show : classes.hide}
|
||||||
<Toolbar>
|
>
|
||||||
<PulsingBadge
|
<Toolbar>
|
||||||
color='secondary'
|
<PulsingBadge
|
||||||
badgeContent={unread}
|
color='secondary'
|
||||||
onClick={() => toggleToolArea()}
|
badgeContent={unread}
|
||||||
>
|
onClick={() => toggleToolArea()}
|
||||||
<IconButton
|
|
||||||
color='inherit'
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'label.openDrawer',
|
|
||||||
defaultMessage : 'Open drawer'
|
|
||||||
})}
|
|
||||||
className={classes.menuButton}
|
|
||||||
>
|
|
||||||
<MenuIcon />
|
|
||||||
</IconButton>
|
|
||||||
</PulsingBadge>
|
|
||||||
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
|
||||||
<Typography
|
|
||||||
className={classes.title}
|
|
||||||
variant='h6'
|
|
||||||
color='inherit'
|
|
||||||
noWrap
|
|
||||||
>
|
|
||||||
{ window.config.title ? window.config.title : 'Multiparty meeting' }
|
|
||||||
</Typography>
|
|
||||||
<div className={classes.grow} />
|
|
||||||
<div className={classes.actionButtons}>
|
|
||||||
{ fullscreenEnabled &&
|
|
||||||
<Tooltip title={fullscreenTooltip}>
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.enterFullscreen',
|
|
||||||
defaultMessage : 'Enter fullscreen'
|
|
||||||
})}
|
|
||||||
className={classes.actionButton}
|
|
||||||
color='inherit'
|
|
||||||
onClick={onFullscreen}
|
|
||||||
>
|
|
||||||
{ fullscreen ?
|
|
||||||
<FullScreenExitIcon />
|
|
||||||
:
|
|
||||||
<FullScreenIcon />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id : 'tooltip.participants',
|
|
||||||
defaultMessage : 'Show participants'
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
color='inherit'
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'label.openDrawer',
|
||||||
|
defaultMessage : 'Open drawer'
|
||||||
|
})}
|
||||||
|
className={classes.menuButton}
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
</PulsingBadge>
|
||||||
|
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
||||||
|
<Typography
|
||||||
|
className={classes.title}
|
||||||
|
variant='h6'
|
||||||
|
color='inherit'
|
||||||
|
noWrap
|
||||||
|
>
|
||||||
|
{ window.config.title ? window.config.title : 'Multiparty meeting' }
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.grow} />
|
||||||
|
<div className={classes.actionButtons}>
|
||||||
|
<IconButton
|
||||||
|
aria-haspopup='true'
|
||||||
|
onClick={handleMoreActionsOpen}
|
||||||
|
color='inherit'
|
||||||
|
>
|
||||||
|
<ExtensionIcon />
|
||||||
|
</IconButton>
|
||||||
|
{ fullscreenEnabled &&
|
||||||
|
<Tooltip title={fullscreenTooltip}>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.enterFullscreen',
|
||||||
|
defaultMessage : 'Enter fullscreen'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
onClick={onFullscreen}
|
||||||
|
>
|
||||||
|
{ fullscreen ?
|
||||||
|
<FullScreenExitIcon />
|
||||||
|
:
|
||||||
|
<FullScreenIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
id : 'tooltip.participants',
|
id : 'tooltip.participants',
|
||||||
defaultMessage : 'Show participants'
|
defaultMessage : 'Show participants'
|
||||||
})}
|
})}
|
||||||
color='inherit'
|
|
||||||
onClick={() => openUsersTab()}
|
|
||||||
>
|
>
|
||||||
<Badge
|
<IconButton
|
||||||
color='primary'
|
aria-label={intl.formatMessage({
|
||||||
badgeContent={peersLength + 1}
|
id : 'tooltip.participants',
|
||||||
|
defaultMessage : 'Show participants'
|
||||||
|
})}
|
||||||
|
color='inherit'
|
||||||
|
onClick={() => openUsersTab()}
|
||||||
>
|
>
|
||||||
<PeopleIcon />
|
<Badge
|
||||||
</Badge>
|
color='primary'
|
||||||
</IconButton>
|
badgeContent={peersLength + 1}
|
||||||
</Tooltip>
|
>
|
||||||
<Tooltip
|
<PeopleIcon />
|
||||||
title={intl.formatMessage({
|
</Badge>
|
||||||
id : 'tooltip.settings',
|
</IconButton>
|
||||||
defaultMessage : 'Show settings'
|
</Tooltip>
|
||||||
})}
|
<Tooltip
|
||||||
>
|
title={intl.formatMessage({
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.settings',
|
id : 'tooltip.settings',
|
||||||
defaultMessage : 'Show settings'
|
defaultMessage : 'Show settings'
|
||||||
})}
|
})}
|
||||||
className={classes.actionButton}
|
|
||||||
color='inherit'
|
|
||||||
onClick={() => setSettingsOpen(!room.settingsOpen)}
|
|
||||||
>
|
|
||||||
<SettingsIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title={lockTooltip}>
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.lockRoom',
|
|
||||||
defaultMessage : 'Lock room'
|
|
||||||
})}
|
|
||||||
className={classes.actionButton}
|
|
||||||
color='inherit'
|
|
||||||
disabled={!canLock}
|
|
||||||
onClick={() =>
|
|
||||||
{
|
|
||||||
if (room.locked)
|
|
||||||
{
|
|
||||||
roomClient.unlockRoom();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
roomClient.lockRoom();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ room.locked ?
|
|
||||||
<LockIcon />
|
|
||||||
:
|
|
||||||
<LockOpenIcon />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
{ lobbyPeers.length > 0 &&
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id : 'tooltip.lobby',
|
|
||||||
defaultMessage : 'Show lobby'
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'tooltip.lobby',
|
id : 'tooltip.settings',
|
||||||
defaultMessage : 'Show lobby'
|
defaultMessage : 'Show settings'
|
||||||
})}
|
|
||||||
color='inherit'
|
|
||||||
disabled={!canPromote}
|
|
||||||
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
|
|
||||||
>
|
|
||||||
<PulsingBadge
|
|
||||||
color='secondary'
|
|
||||||
badgeContent={lobbyPeers.length}
|
|
||||||
>
|
|
||||||
<SecurityIcon />
|
|
||||||
</PulsingBadge>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
{ loginEnabled &&
|
|
||||||
<Tooltip title={loginTooltip}>
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.login',
|
|
||||||
defaultMessage : 'Log in'
|
|
||||||
})}
|
})}
|
||||||
className={classes.actionButton}
|
className={classes.actionButton}
|
||||||
color='inherit'
|
color='inherit'
|
||||||
onClick={() =>
|
onClick={() => setSettingsOpen(!room.settingsOpen)}
|
||||||
{
|
|
||||||
loggedIn ? roomClient.logout() : roomClient.login();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{ myPicture ?
|
<SettingsIcon />
|
||||||
<Avatar src={myPicture} />
|
|
||||||
:
|
|
||||||
<AccountCircle className={loggedIn && classes.green} />
|
|
||||||
}
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
<Tooltip title={lockTooltip}>
|
||||||
<div className={classes.divider} />
|
<span className={classes.disabledButton}>
|
||||||
<Button
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.lockRoom',
|
||||||
|
defaultMessage : 'Lock room'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
disabled={!canLock}
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
if (room.locked)
|
||||||
|
{
|
||||||
|
roomClient.unlockRoom();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
roomClient.lockRoom();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ room.locked ?
|
||||||
|
<LockIcon />
|
||||||
|
:
|
||||||
|
<LockOpenIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{ lobbyPeers.length > 0 &&
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id : 'tooltip.lobby',
|
||||||
|
defaultMessage : 'Show lobby'
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className={classes.disabledButton}>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.lobby',
|
||||||
|
defaultMessage : 'Show lobby'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
disabled={!canPromote}
|
||||||
|
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
|
||||||
|
>
|
||||||
|
<PulsingBadge
|
||||||
|
color='secondary'
|
||||||
|
badgeContent={lobbyPeers.length}
|
||||||
|
>
|
||||||
|
<SecurityIcon />
|
||||||
|
</PulsingBadge>
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
{ loginEnabled &&
|
||||||
|
<Tooltip title={loginTooltip}>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.login',
|
||||||
|
defaultMessage : 'Log in'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
loggedIn ? roomClient.logout() : roomClient.login();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ myPicture ?
|
||||||
|
<Avatar src={myPicture} />
|
||||||
|
:
|
||||||
|
<AccountCircle className={loggedIn ? classes.green : null} />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
<div className={classes.divider} />
|
||||||
|
<Button
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'label.leave',
|
||||||
|
defaultMessage : 'Leave'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
variant='contained'
|
||||||
|
color='secondary'
|
||||||
|
onClick={() => roomClient.close()}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='label.leave'
|
||||||
|
defaultMessage='Leave'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
<Menu
|
||||||
|
anchorEl={moreActionsElement}
|
||||||
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
||||||
|
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||||
|
open={isMoreActionsMenuOpen}
|
||||||
|
onClose={handleMoreActionsClose}
|
||||||
|
getContentAnchorEl={null}
|
||||||
|
>
|
||||||
|
<MenuItem
|
||||||
|
dense
|
||||||
|
disabled={!canProduceExtraVideo}
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
handleMoreActionsClose();
|
||||||
|
setExtraVideoOpen(!room.extraVideoOpen);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VideoCallIcon
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'label.leave',
|
id : 'label.addVideo',
|
||||||
defaultMessage : 'Leave'
|
defaultMessage : 'Add video'
|
||||||
})}
|
})}
|
||||||
className={classes.actionButton}
|
/>
|
||||||
variant='contained'
|
<p className={classes.moreAction}>
|
||||||
color='secondary'
|
|
||||||
onClick={() => roomClient.close()}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='label.leave'
|
id='label.addVideo'
|
||||||
defaultMessage='Leave'
|
defaultMessage='Add video'
|
||||||
/>
|
/>
|
||||||
</Button>
|
</p>
|
||||||
</div>
|
</MenuItem>
|
||||||
</Toolbar>
|
</Menu>
|
||||||
</AppBar>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
TopBar.propTypes =
|
TopBar.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.object.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
peersLength : PropTypes.number,
|
peersLength : PropTypes.number,
|
||||||
lobbyPeers : PropTypes.array,
|
lobbyPeers : PropTypes.array,
|
||||||
permanentTopBar : PropTypes.bool,
|
permanentTopBar : PropTypes.bool,
|
||||||
myPicture : PropTypes.string,
|
myPicture : PropTypes.string,
|
||||||
loggedIn : PropTypes.bool.isRequired,
|
loggedIn : PropTypes.bool.isRequired,
|
||||||
loginEnabled : PropTypes.bool.isRequired,
|
loginEnabled : PropTypes.bool.isRequired,
|
||||||
fullscreenEnabled : PropTypes.bool,
|
fullscreenEnabled : PropTypes.bool,
|
||||||
fullscreen : PropTypes.bool,
|
fullscreen : PropTypes.bool,
|
||||||
onFullscreen : PropTypes.func.isRequired,
|
onFullscreen : PropTypes.func.isRequired,
|
||||||
setToolbarsVisible : PropTypes.func.isRequired,
|
setToolbarsVisible : PropTypes.func.isRequired,
|
||||||
setSettingsOpen : PropTypes.func.isRequired,
|
setSettingsOpen : PropTypes.func.isRequired,
|
||||||
setLockDialogOpen : PropTypes.func.isRequired,
|
setExtraVideoOpen : PropTypes.func.isRequired,
|
||||||
toggleToolArea : PropTypes.func.isRequired,
|
setLockDialogOpen : PropTypes.func.isRequired,
|
||||||
openUsersTab : PropTypes.func.isRequired,
|
toggleToolArea : PropTypes.func.isRequired,
|
||||||
unread : PropTypes.number.isRequired,
|
openUsersTab : PropTypes.func.isRequired,
|
||||||
canLock : PropTypes.bool.isRequired,
|
unread : PropTypes.number.isRequired,
|
||||||
canPromote : PropTypes.bool.isRequired,
|
canProduceExtraVideo : PropTypes.bool.isRequired,
|
||||||
classes : PropTypes.object.isRequired,
|
canLock : PropTypes.bool.isRequired,
|
||||||
theme : PropTypes.object.isRequired
|
canPromote : PropTypes.bool.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired,
|
||||||
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
|
|
@ -401,7 +477,10 @@ const mapStateToProps = (state) =>
|
||||||
loginEnabled : state.me.loginEnabled,
|
loginEnabled : state.me.loginEnabled,
|
||||||
myPicture : state.me.picture,
|
myPicture : state.me.picture,
|
||||||
unread : state.toolarea.unreadMessages +
|
unread : state.toolarea.unreadMessages +
|
||||||
state.toolarea.unreadFiles,
|
state.toolarea.unreadFiles + raisedHandsSelector(state),
|
||||||
|
canProduceExtraVideo :
|
||||||
|
state.me.roles.some((role) =>
|
||||||
|
state.room.permissionsFromRoles.EXTRA_VIDEO.includes(role)),
|
||||||
canLock :
|
canLock :
|
||||||
state.me.roles.some((role) =>
|
state.me.roles.some((role) =>
|
||||||
state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)),
|
state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)),
|
||||||
|
|
@ -418,11 +497,15 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
},
|
},
|
||||||
setSettingsOpen : (settingsOpen) =>
|
setSettingsOpen : (settingsOpen) =>
|
||||||
{
|
{
|
||||||
dispatch(roomActions.setSettingsOpen({ settingsOpen }));
|
dispatch(roomActions.setSettingsOpen(settingsOpen));
|
||||||
|
},
|
||||||
|
setExtraVideoOpen : (extraVideoOpen) =>
|
||||||
|
{
|
||||||
|
dispatch(roomActions.setExtraVideoOpen(extraVideoOpen));
|
||||||
},
|
},
|
||||||
setLockDialogOpen : (lockDialogOpen) =>
|
setLockDialogOpen : (lockDialogOpen) =>
|
||||||
{
|
{
|
||||||
dispatch(roomActions.setLockDialogOpen({ lockDialogOpen }));
|
dispatch(roomActions.setLockDialogOpen(lockDialogOpen));
|
||||||
},
|
},
|
||||||
toggleToolArea : () =>
|
toggleToolArea : () =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ const DialogTitle = withStyles(styles)((props) =>
|
||||||
<Avatar src={myPicture} className={classes.largeAvatar} />
|
<Avatar src={myPicture} className={classes.largeAvatar} />
|
||||||
:
|
:
|
||||||
<AccountCircle
|
<AccountCircle
|
||||||
className={classnames(classes.largeIcon, loggedIn && classes.green)}
|
className={classnames(classes.largeIcon, loggedIn ? classes.green : null)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ const Message = (props) =>
|
||||||
<Typography variant='caption'>
|
<Typography variant='caption'>
|
||||||
{ self ?
|
{ self ?
|
||||||
intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'room.me',
|
id : 'room.me',
|
||||||
defaultMessage : 'Me'
|
defaultMessage : 'Me'
|
||||||
})
|
})
|
||||||
:
|
:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { raisedHandsSelector } from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import * as toolareaActions from '../../actions/toolareaActions';
|
import * as toolareaActions from '../../actions/toolareaActions';
|
||||||
|
|
@ -51,6 +52,7 @@ const MeetingDrawer = (props) =>
|
||||||
currentToolTab,
|
currentToolTab,
|
||||||
unreadMessages,
|
unreadMessages,
|
||||||
unreadFiles,
|
unreadFiles,
|
||||||
|
raisedHands,
|
||||||
closeDrawer,
|
closeDrawer,
|
||||||
setToolTab,
|
setToolTab,
|
||||||
classes,
|
classes,
|
||||||
|
|
@ -93,10 +95,14 @@ const MeetingDrawer = (props) =>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label={intl.formatMessage({
|
label={
|
||||||
id : 'label.participants',
|
<Badge color='secondary' badgeContent={raisedHands}>
|
||||||
defaultMessage : 'Participants'
|
{intl.formatMessage({
|
||||||
})}
|
id : 'label.participants',
|
||||||
|
defaultMessage : 'Participants'
|
||||||
|
})}
|
||||||
|
</Badge>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<IconButton onClick={closeDrawer}>
|
<IconButton onClick={closeDrawer}>
|
||||||
|
|
@ -116,16 +122,21 @@ MeetingDrawer.propTypes =
|
||||||
setToolTab : PropTypes.func.isRequired,
|
setToolTab : PropTypes.func.isRequired,
|
||||||
unreadMessages : PropTypes.number.isRequired,
|
unreadMessages : PropTypes.number.isRequired,
|
||||||
unreadFiles : PropTypes.number.isRequired,
|
unreadFiles : PropTypes.number.isRequired,
|
||||||
|
raisedHands : PropTypes.number.isRequired,
|
||||||
closeDrawer : PropTypes.func.isRequired,
|
closeDrawer : PropTypes.func.isRequired,
|
||||||
classes : PropTypes.object.isRequired,
|
classes : PropTypes.object.isRequired,
|
||||||
theme : PropTypes.object.isRequired
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) =>
|
||||||
currentToolTab : state.toolarea.currentToolTab,
|
{
|
||||||
unreadMessages : state.toolarea.unreadMessages,
|
return {
|
||||||
unreadFiles : state.toolarea.unreadFiles
|
currentToolTab : state.toolarea.currentToolTab,
|
||||||
});
|
unreadMessages : state.toolarea.unreadMessages,
|
||||||
|
unreadFiles : state.toolarea.unreadFiles,
|
||||||
|
raisedHands : raisedHandsSelector(state)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setToolTab : toolareaActions.setToolTab
|
setToolTab : toolareaActions.setToolTab
|
||||||
|
|
@ -141,7 +152,8 @@ export default connect(
|
||||||
return (
|
return (
|
||||||
prev.toolarea.currentToolTab === next.toolarea.currentToolTab &&
|
prev.toolarea.currentToolTab === next.toolarea.currentToolTab &&
|
||||||
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
||||||
prev.toolarea.unreadFiles === next.toolarea.unreadFiles
|
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
|
||||||
|
prev.peers === next.peers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,50 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import classnames from 'classnames';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
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 EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
||||||
import HandIcon from '../../../images/icon-hand-white.svg';
|
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(1),
|
|
||||||
width : '100%',
|
width : '100%',
|
||||||
overflow : 'hidden',
|
overflow : 'hidden',
|
||||||
cursor : 'auto',
|
cursor : 'auto',
|
||||||
display : 'flex'
|
display : 'flex',
|
||||||
},
|
padding : theme.spacing(1)
|
||||||
listPeer :
|
|
||||||
{
|
|
||||||
display : 'flex'
|
|
||||||
},
|
},
|
||||||
avatar :
|
avatar :
|
||||||
{
|
{
|
||||||
borderRadius : '50%',
|
borderRadius : '50%',
|
||||||
height : '2rem'
|
height : '2rem',
|
||||||
|
marginTop : theme.spacing(1)
|
||||||
},
|
},
|
||||||
peerInfo :
|
peerInfo :
|
||||||
{
|
{
|
||||||
fontSize : '1rem',
|
fontSize : '1rem',
|
||||||
border : 'none',
|
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
paddingLeft : theme.spacing(1),
|
paddingLeft : theme.spacing(1),
|
||||||
flexGrow : 1,
|
flexGrow : 1,
|
||||||
alignItems : 'center'
|
alignItems : 'center'
|
||||||
},
|
},
|
||||||
indicators :
|
green :
|
||||||
{
|
{
|
||||||
left : 0,
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
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
|
|
||||||
},
|
|
||||||
'&.raise-hand' :
|
|
||||||
{
|
|
||||||
backgroundImage : `url(${HandIcon})`,
|
|
||||||
opacity : 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListMe = (props) =>
|
const ListMe = (props) =>
|
||||||
{
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
roomClient,
|
||||||
me,
|
me,
|
||||||
settings,
|
settings,
|
||||||
classes
|
classes
|
||||||
|
|
@ -82,29 +53,38 @@ const ListMe = (props) =>
|
||||||
const picture = me.picture || EmptyAvatar;
|
const picture = me.picture || EmptyAvatar;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.listPeer}>
|
<img alt='My avatar' className={classes.avatar} src={picture} />
|
||||||
<img alt='My avatar' className={classes.avatar} src={picture} />
|
|
||||||
|
|
||||||
<div className={classes.peerInfo}>
|
<div className={classes.peerInfo}>
|
||||||
{settings.displayName}
|
{settings.displayName}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={classes.indicators}>
|
|
||||||
{ me.raisedHand &&
|
|
||||||
<div className={classnames(classes.icon, 'raise-hand')} />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.raisedHand',
|
||||||
|
defaultMessage : 'Raise hand'
|
||||||
|
})}
|
||||||
|
className={me.raisedHand ? classes.green : null}
|
||||||
|
disabled={me.raisedHandInProgress}
|
||||||
|
onClick={(e) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
roomClient.setRaisedHand(!me.raisedHand);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PanIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ListMe.propTypes =
|
ListMe.propTypes =
|
||||||
{
|
{
|
||||||
me : appPropTypes.Me.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
settings : PropTypes.object.isRequired,
|
me : appPropTypes.Me.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
settings : PropTypes.object.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
|
|
@ -112,7 +92,7 @@ const mapStateToProps = (state) => ({
|
||||||
settings : state.settings
|
settings : state.settings
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|
@ -125,4 +105,4 @@ export default connect(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)(withStyles(styles)(ListMe));
|
)(withStyles(styles)(ListMe)));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { connect } from 'react-redux';
|
||||||
import { makePeerConsumerSelector } from '../../Selectors';
|
import { makePeerConsumerSelector } from '../../Selectors';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
@ -16,31 +15,26 @@ import ScreenIcon from '@material-ui/icons/ScreenShare';
|
||||||
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
||||||
import ExitIcon from '@material-ui/icons/ExitToApp';
|
import ExitIcon from '@material-ui/icons/ExitToApp';
|
||||||
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
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) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(1),
|
|
||||||
width : '100%',
|
width : '100%',
|
||||||
overflow : 'hidden',
|
overflow : 'hidden',
|
||||||
cursor : 'auto',
|
cursor : 'auto',
|
||||||
display : 'flex'
|
display : 'flex'
|
||||||
},
|
},
|
||||||
listPeer :
|
|
||||||
{
|
|
||||||
display : 'flex'
|
|
||||||
},
|
|
||||||
avatar :
|
avatar :
|
||||||
{
|
{
|
||||||
borderRadius : '50%',
|
borderRadius : '50%',
|
||||||
height : '2rem'
|
height : '2rem',
|
||||||
|
marginTop : theme.spacing(1)
|
||||||
},
|
},
|
||||||
peerInfo :
|
peerInfo :
|
||||||
{
|
{
|
||||||
fontSize : '1rem',
|
fontSize : '1rem',
|
||||||
border : 'none',
|
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
paddingLeft : theme.spacing(1),
|
paddingLeft : theme.spacing(1),
|
||||||
flexGrow : 1,
|
flexGrow : 1,
|
||||||
|
|
@ -48,52 +42,12 @@ const styles = (theme) =>
|
||||||
},
|
},
|
||||||
indicators :
|
indicators :
|
||||||
{
|
{
|
||||||
left : 0,
|
display : 'flex',
|
||||||
top : 0,
|
padding : theme.spacing(1.5)
|
||||||
display : 'flex',
|
|
||||||
flexDirection : 'row',
|
|
||||||
justifyContent : 'flex-start',
|
|
||||||
alignItems : 'center',
|
|
||||||
transition : 'opacity 0.3s'
|
|
||||||
},
|
},
|
||||||
icon :
|
green :
|
||||||
{
|
{
|
||||||
flex : '0 0 auto',
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
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
|
|
||||||
},
|
|
||||||
'&.raise-hand' :
|
|
||||||
{
|
|
||||||
backgroundImage : `url(${HandIcon})`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
controls :
|
|
||||||
{
|
|
||||||
float : 'right',
|
|
||||||
display : 'flex',
|
|
||||||
flexDirection : 'row',
|
|
||||||
justifyContent : 'flex-start',
|
|
||||||
alignItems : 'center'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -140,106 +94,96 @@ const ListPeer = (props) =>
|
||||||
{peer.displayName}
|
{peer.displayName}
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.indicators}>
|
<div className={classes.indicators}>
|
||||||
{ peer.raiseHandState &&
|
{ peer.raisedHand &&
|
||||||
<div className={
|
<PanIcon className={classes.green} />
|
||||||
classnames(
|
|
||||||
classes.icon, 'raise-hand', {
|
|
||||||
on : peer.raiseHandState,
|
|
||||||
off : !peer.raiseHandState
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
{ screenConsumer &&
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.muteScreenSharing',
|
||||||
|
defaultMessage : 'Mute participant share'
|
||||||
|
})}
|
||||||
|
color={screenVisible ? 'primary' : 'secondary'}
|
||||||
|
disabled={peer.peerScreenInProgress}
|
||||||
|
onClick={(e) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
screenVisible ?
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ screenVisible ?
|
||||||
|
<ScreenIcon />
|
||||||
|
:
|
||||||
|
<ScreenOffIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.muteParticipantVideo',
|
||||||
|
defaultMessage : 'Mute participant video'
|
||||||
|
})}
|
||||||
|
color={webcamEnabled ? 'primary' : 'secondary'}
|
||||||
|
disabled={peer.peerVideoInProgress}
|
||||||
|
onClick={(e) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
webcamEnabled ?
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'webcam', true) :
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'webcam', false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ webcamEnabled ?
|
||||||
|
<VideocamIcon />
|
||||||
|
:
|
||||||
|
<VideocamOffIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.muteParticipant',
|
||||||
|
defaultMessage : 'Mute participant'
|
||||||
|
})}
|
||||||
|
color={micEnabled ? 'primary' : 'secondary'}
|
||||||
|
disabled={peer.peerAudioInProgress}
|
||||||
|
onClick={(e) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
micEnabled ?
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ micEnabled ?
|
||||||
|
<VolumeUpIcon />
|
||||||
|
:
|
||||||
|
<VolumeOffIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
{ isModerator &&
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.kickParticipant',
|
||||||
|
defaultMessage : 'Kick out participant'
|
||||||
|
})}
|
||||||
|
disabled={peer.peerKickInProgress}
|
||||||
|
onClick={(e) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
roomClient.kickPeer(peer.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ExitIcon />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
{children}
|
{children}
|
||||||
<div className={classes.controls}>
|
|
||||||
{ screenConsumer &&
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.muteScreenSharing',
|
|
||||||
defaultMessage : 'Mute participant share'
|
|
||||||
})}
|
|
||||||
color={screenVisible ? 'primary' : 'secondary'}
|
|
||||||
disabled={peer.peerScreenInProgress}
|
|
||||||
onClick={(e) =>
|
|
||||||
{
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
screenVisible ?
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ screenVisible ?
|
|
||||||
<ScreenIcon />
|
|
||||||
:
|
|
||||||
<ScreenOffIcon />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.muteParticipantVideo',
|
|
||||||
defaultMessage : 'Mute participant video'
|
|
||||||
})}
|
|
||||||
color={webcamEnabled ? 'primary' : 'secondary'}
|
|
||||||
disabled={peer.peerVideoInProgress}
|
|
||||||
onClick={(e) =>
|
|
||||||
{
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
webcamEnabled ?
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'webcam', true) :
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'webcam', false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ webcamEnabled ?
|
|
||||||
<VideocamIcon />
|
|
||||||
:
|
|
||||||
<VideocamOffIcon />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.muteParticipant',
|
|
||||||
defaultMessage : 'Mute participant'
|
|
||||||
})}
|
|
||||||
color={micEnabled ? 'primary' : 'secondary'}
|
|
||||||
disabled={peer.peerAudioInProgress}
|
|
||||||
onClick={(e) =>
|
|
||||||
{
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
micEnabled ?
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ micEnabled ?
|
|
||||||
<VolumeUpIcon />
|
|
||||||
:
|
|
||||||
<VolumeOffIcon />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
{ isModerator &&
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.kickParticipant',
|
|
||||||
defaultMessage : 'Kick out participant'
|
|
||||||
})}
|
|
||||||
disabled={peer.peerKickInProgress}
|
|
||||||
onClick={(e) =>
|
|
||||||
{
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
roomClient.kickPeer(peer.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ExitIcon />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,12 @@ import Peer from '../Containers/Peer';
|
||||||
import SpeakerPeer from '../Containers/SpeakerPeer';
|
import SpeakerPeer from '../Containers/SpeakerPeer';
|
||||||
import Grid from '@material-ui/core/Grid';
|
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 = () =>
|
const styles = () =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
|
|
@ -20,24 +26,22 @@ const styles = () =>
|
||||||
width : '100%',
|
width : '100%',
|
||||||
display : 'grid',
|
display : 'grid',
|
||||||
gridTemplateColumns : '1fr',
|
gridTemplateColumns : '1fr',
|
||||||
gridTemplateRows : '1.6fr minmax(0, 0.4fr)'
|
gridTemplateRows : '1fr 0.25fr'
|
||||||
},
|
},
|
||||||
speaker :
|
speaker :
|
||||||
{
|
{
|
||||||
gridArea : '1 / 1 / 2 / 2',
|
gridArea : '1 / 1 / 1 / 1',
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
justifyContent : 'center',
|
justifyContent : 'center',
|
||||||
alignItems : 'center',
|
alignItems : 'center'
|
||||||
paddingTop : 40
|
|
||||||
},
|
},
|
||||||
filmStrip :
|
filmStrip :
|
||||||
{
|
{
|
||||||
gridArea : '2 / 1 / 3 / 2'
|
gridArea : '2 / 1 / 2 / 1'
|
||||||
},
|
},
|
||||||
filmItem :
|
filmItem :
|
||||||
{
|
{
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
marginLeft : '6px',
|
|
||||||
border : 'var(--peer-border)',
|
border : 'var(--peer-border)',
|
||||||
'&.selected' :
|
'&.selected' :
|
||||||
{
|
{
|
||||||
|
|
@ -47,6 +51,16 @@ const styles = () =>
|
||||||
{
|
{
|
||||||
opacity : '0.6'
|
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.resizeTimeout = null;
|
||||||
|
|
||||||
|
this.rootContainer = React.createRef();
|
||||||
|
|
||||||
this.activePeerContainer = React.createRef();
|
this.activePeerContainer = React.createRef();
|
||||||
|
|
||||||
this.filmStripContainer = React.createRef();
|
this.filmStripContainer = React.createRef();
|
||||||
|
|
@ -105,24 +121,35 @@ class Filmstrip extends React.PureComponent
|
||||||
{
|
{
|
||||||
const newState = {};
|
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;
|
const speaker = this.activePeerContainer.current;
|
||||||
|
|
||||||
if (speaker)
|
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()))
|
if (this.isSharingCamera(this.getActivePeerId()))
|
||||||
{
|
{
|
||||||
speakerWidth /= 2;
|
speakerWidth /= 2;
|
||||||
speakerHeight = (speakerWidth / 4) * 3;
|
speakerHeight = speakerWidth / RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (speakerHeight > (speaker.clientHeight - 60))
|
if (speakerHeight > (availableSpeakerHeight - PADDING_V))
|
||||||
{
|
{
|
||||||
speakerHeight = (speaker.clientHeight - 60);
|
speakerHeight = (availableSpeakerHeight - PADDING_V);
|
||||||
speakerWidth = (speakerHeight / 3) * 4;
|
speakerWidth = speakerHeight * RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
newState.speakerWidth = speakerWidth;
|
newState.speakerWidth = speakerWidth;
|
||||||
|
|
@ -133,14 +160,18 @@ class Filmstrip extends React.PureComponent
|
||||||
|
|
||||||
if (filmStrip)
|
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;
|
filmStripWidth = (availableWidth - FILMSTRING_PADDING_H) /
|
||||||
filmStripHeight = (filmStripWidth / 4) * 3;
|
this.props.boxes;
|
||||||
|
filmStripHeight = filmStripWidth / RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
newState.filmStripWidth = filmStripWidth;
|
newState.filmStripWidth = filmStripWidth;
|
||||||
|
|
@ -172,27 +203,21 @@ class Filmstrip extends React.PureComponent
|
||||||
window.removeEventListener('resize', this.updateDimensions);
|
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)
|
componentDidUpdate(prevProps)
|
||||||
{
|
{
|
||||||
if (prevProps !== this.props)
|
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();
|
this.updateDimensions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +230,8 @@ class Filmstrip extends React.PureComponent
|
||||||
myId,
|
myId,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
spotlights,
|
spotlights,
|
||||||
|
toolbarsVisible,
|
||||||
|
permanentTopBar,
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
|
@ -223,7 +250,14 @@ class Filmstrip extends React.PureComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div
|
||||||
|
className={classnames(
|
||||||
|
classes.root,
|
||||||
|
toolbarsVisible || permanentTopBar ?
|
||||||
|
classes.showingToolBar : classes.hiddenToolBar
|
||||||
|
)}
|
||||||
|
ref={this.rootContainer}
|
||||||
|
>
|
||||||
<div className={classes.speaker} ref={this.activePeerContainer}>
|
<div className={classes.speaker} ref={this.activePeerContainer}>
|
||||||
{ peers[activePeerId] &&
|
{ peers[activePeerId] &&
|
||||||
<SpeakerPeer
|
<SpeakerPeer
|
||||||
|
|
@ -296,6 +330,8 @@ Filmstrip.propTypes = {
|
||||||
selectedPeerId : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlights : PropTypes.array.isRequired,
|
spotlights : PropTypes.array.isRequired,
|
||||||
boxes : PropTypes.number,
|
boxes : PropTypes.number,
|
||||||
|
toolbarsVisible : PropTypes.bool.isRequired,
|
||||||
|
permanentTopBar : PropTypes.bool,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -308,7 +344,9 @@ const mapStateToProps = (state) =>
|
||||||
consumers : state.consumers,
|
consumers : state.consumers,
|
||||||
myId : state.me.id,
|
myId : state.me.id,
|
||||||
spotlights : state.room.spotlights,
|
spotlights : state.room.spotlights,
|
||||||
boxes : videoBoxesSelector(state)
|
boxes : videoBoxesSelector(state),
|
||||||
|
toolbarsVisible : state.room.toolbarsVisible,
|
||||||
|
permanentTopBar : state.settings.permanentTopBar
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -322,6 +360,8 @@ export default withRoomContext(connect(
|
||||||
return (
|
return (
|
||||||
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||||
prev.room.selectedPeerId === next.room.selectedPeerId &&
|
prev.room.selectedPeerId === next.room.selectedPeerId &&
|
||||||
|
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
|
||||||
|
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.room.spotlights === next.room.spotlights &&
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,15 @@ export default class PeerAudio extends React.PureComponent
|
||||||
this._setOutputDevice(audioOutputDevice);
|
this._setOutputDevice(audioOutputDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
componentDidUpdate(prevProps)
|
||||||
UNSAFE_componentWillReceiveProps(nextProps)
|
|
||||||
{
|
{
|
||||||
const { audioTrack, audioOutputDevice } = nextProps;
|
if (prevProps !== this.props)
|
||||||
|
{
|
||||||
|
const { audioTrack, audioOutputDevice } = this.props;
|
||||||
|
|
||||||
this._setTrack(audioTrack);
|
this._setTrack(audioTrack);
|
||||||
this._setOutputDevice(audioOutputDevice);
|
this._setOutputDevice(audioOutputDevice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setTrack(audioTrack)
|
_setTrack(audioTrack)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import LockDialog from './AccessControl/LockDialog/LockDialog';
|
||||||
import Settings from './Settings/Settings';
|
import Settings from './Settings/Settings';
|
||||||
import TopBar from './Controls/TopBar';
|
import TopBar from './Controls/TopBar';
|
||||||
import WakeLock from 'react-wakelock-react16';
|
import WakeLock from 'react-wakelock-react16';
|
||||||
|
import ExtraVideo from './Controls/ExtraVideo';
|
||||||
|
|
||||||
const TIMEOUT = 5 * 1000;
|
const TIMEOUT = 5 * 1000;
|
||||||
|
|
||||||
|
|
@ -217,6 +218,10 @@ class Room extends React.PureComponent
|
||||||
{ room.settingsOpen &&
|
{ room.settingsOpen &&
|
||||||
<Settings />
|
<Settings />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ room.extraVideoOpen &&
|
||||||
|
<ExtraVideo />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ export const screenProducersSelector = createSelector(
|
||||||
(producers) => Object.values(producers).filter((producer) => producer.source === 'screen')
|
(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(
|
export const micProducerSelector = createSelector(
|
||||||
producersSelect,
|
producersSelect,
|
||||||
(producers) => Object.values(producers).find((producer) => producer.source === 'mic')
|
(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')
|
(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(
|
export const passiveMicConsumerSelector = createSelector(
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
consumersSelect,
|
consumersSelect,
|
||||||
|
|
@ -106,24 +129,41 @@ export const passivePeersSelector = createSelector(
|
||||||
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
|
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const raisedHandsSelector = createSelector(
|
||||||
|
peersValueSelector,
|
||||||
|
(peers) => peers.reduce((a, b) => (a + (b.raisedHand ? 1 : 0)), 0)
|
||||||
|
);
|
||||||
|
|
||||||
export const videoBoxesSelector = createSelector(
|
export const videoBoxesSelector = createSelector(
|
||||||
spotlightsLengthSelector,
|
spotlightsLengthSelector,
|
||||||
screenProducersSelector,
|
screenProducersSelector,
|
||||||
screenConsumerSelector,
|
spotlightScreenConsumerSelector,
|
||||||
(spotlightsLength, screenProducers, screenConsumers) =>
|
extraVideoProducersSelector,
|
||||||
spotlightsLength + 1 + screenProducers.length + screenConsumers.length
|
spotlightExtraVideoConsumerSelector,
|
||||||
|
(
|
||||||
|
spotlightsLength,
|
||||||
|
screenProducers,
|
||||||
|
screenConsumers,
|
||||||
|
extraVideoProducers,
|
||||||
|
extraVideoConsumers
|
||||||
|
) =>
|
||||||
|
spotlightsLength + 1 + screenProducers.length +
|
||||||
|
screenConsumers.length + extraVideoProducers.length +
|
||||||
|
extraVideoConsumers.length
|
||||||
);
|
);
|
||||||
|
|
||||||
export const meProducersSelector = createSelector(
|
export const meProducersSelector = createSelector(
|
||||||
micProducerSelector,
|
micProducerSelector,
|
||||||
webcamProducerSelector,
|
webcamProducerSelector,
|
||||||
screenProducerSelector,
|
screenProducerSelector,
|
||||||
(micProducer, webcamProducer, screenProducer) =>
|
extraVideoProducersSelector,
|
||||||
|
(micProducer, webcamProducer, screenProducer, extraVideoProducers) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
micProducer,
|
micProducer,
|
||||||
webcamProducer,
|
webcamProducer,
|
||||||
screenProducer
|
screenProducer,
|
||||||
|
extraVideoProducers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -146,8 +186,10 @@ export const makePeerConsumerSelector = () =>
|
||||||
consumersArray.find((consumer) => consumer.source === 'webcam');
|
consumersArray.find((consumer) => consumer.source === 'webcam');
|
||||||
const screenConsumer =
|
const screenConsumer =
|
||||||
consumersArray.find((consumer) => consumer.source === 'screen');
|
consumersArray.find((consumer) => consumer.source === 'screen');
|
||||||
|
const extraVideoConsumers =
|
||||||
|
consumersArray.filter((consumer) => consumer.source === 'extravideo');
|
||||||
|
|
||||||
return { micConsumer, webcamConsumer, screenConsumer };
|
return { micConsumer, webcamConsumer, screenConsumer, extraVideoConsumers };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
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,
|
||||||
|
onToggleNotificationSounds,
|
||||||
|
classes
|
||||||
|
}) =>
|
||||||
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<FormControlLabel
|
||||||
|
className={classes.setting}
|
||||||
|
control={<Checkbox checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id : 'settings.advancedMode',
|
||||||
|
defaultMessage : 'Advanced mode'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
className={classes.setting}
|
||||||
|
control={<Checkbox checked={settings.notificationSounds} onChange={onToggleNotificationSounds} value='notificationSounds' />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id : 'settings.notificationSounds',
|
||||||
|
defaultMessage : 'Notification sounds'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
{ !window.config.lockLastN &&
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={settings.lastN || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
{
|
||||||
|
if (event.target.value)
|
||||||
|
roomClient.changeMaxSpotlights(event.target.value);
|
||||||
|
}}
|
||||||
|
name='Last N'
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
>
|
||||||
|
{ Array.from(
|
||||||
|
{ length: window.config.maxLastN || 10 },
|
||||||
|
(_, i) => i + 1
|
||||||
|
).map((lastN) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={lastN} value={lastN}>
|
||||||
|
{lastN}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
<FormattedMessage
|
||||||
|
id='settings.lastn'
|
||||||
|
defaultMessage='Number of visible videos'
|
||||||
|
/>
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AdvancedSettings.propTypes =
|
||||||
|
{
|
||||||
|
roomClient : PropTypes.any.isRequired,
|
||||||
|
settings : PropTypes.object.isRequired,
|
||||||
|
onToggleAdvancedMode : PropTypes.func.isRequired,
|
||||||
|
onToggleNotificationSounds : PropTypes.func.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) =>
|
||||||
|
({
|
||||||
|
settings : state.settings
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
onToggleAdvancedMode : settingsActions.toggleAdvancedMode,
|
||||||
|
onToggleNotificationSounds : settingsActions.toggleNotificationSounds
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withRoomContext(connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
areStatesEqual : (next, prev) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
prev.settings === next.settings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)(withStyles(styles)(AdvancedSettings)));
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
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,
|
||||||
|
onToggleHiddenControls,
|
||||||
|
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 (
|
||||||
|
<React.Fragment>
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={room.mode || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
{
|
||||||
|
if (event.target.value)
|
||||||
|
handleChangeMode(event.target.value);
|
||||||
|
}}
|
||||||
|
name={intl.formatMessage({
|
||||||
|
id : 'settings.layout',
|
||||||
|
defaultMessage : 'Room layout'
|
||||||
|
})}
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
>
|
||||||
|
{ modes.map((mode, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={mode.value}>
|
||||||
|
{mode.label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
<FormattedMessage
|
||||||
|
id='settings.selectRoomLayout'
|
||||||
|
defaultMessage='Select room layout'
|
||||||
|
/>
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
<FormControlLabel
|
||||||
|
className={classes.setting}
|
||||||
|
control={<Checkbox checked={settings.permanentTopBar} onChange={onTogglePermanentTopBar} value='permanentTopBar' />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id : 'settings.permanentTopBar',
|
||||||
|
defaultMessage : 'Permanent top bar'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
className={classes.setting}
|
||||||
|
control={<Checkbox checked={settings.hiddenControls} onChange={onToggleHiddenControls} value='hiddenControls' />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id : 'settings.hiddenControls',
|
||||||
|
defaultMessage : 'Hidden media controls'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppearenceSettings.propTypes =
|
||||||
|
{
|
||||||
|
room : appPropTypes.Room.isRequired,
|
||||||
|
settings : PropTypes.object.isRequired,
|
||||||
|
onTogglePermanentTopBar : PropTypes.func.isRequired,
|
||||||
|
onToggleHiddenControls : 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,
|
||||||
|
onToggleHiddenControls : settingsActions.toggleHiddenControls,
|
||||||
|
handleChangeMode : roomActions.setDisplayMode
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
areStatesEqual : (next, prev) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
prev.room === next.room &&
|
||||||
|
prev.settings === next.settings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)(withStyles(styles)(AppearenceSettings));
|
||||||
|
|
@ -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 (
|
||||||
|
<React.Fragment>
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={settings.selectedWebcam || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
{
|
||||||
|
if (event.target.value)
|
||||||
|
roomClient.changeWebcam(event.target.value);
|
||||||
|
}}
|
||||||
|
displayEmpty
|
||||||
|
name={intl.formatMessage({
|
||||||
|
id : 'settings.camera',
|
||||||
|
defaultMessage : 'Camera'
|
||||||
|
})}
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
disabled={webcams.length === 0 || me.webcamInProgress}
|
||||||
|
>
|
||||||
|
{ webcams.map((webcam, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{ webcams.length > 0 ?
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.selectCamera',
|
||||||
|
defaultMessage : 'Select video device'
|
||||||
|
})
|
||||||
|
:
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.cantSelectCamera',
|
||||||
|
defaultMessage : 'Unable to select video device'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={settings.selectedAudioDevice || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
{
|
||||||
|
if (event.target.value)
|
||||||
|
roomClient.changeAudioDevice(event.target.value);
|
||||||
|
}}
|
||||||
|
displayEmpty
|
||||||
|
name={intl.formatMessage({
|
||||||
|
id : 'settings.audio',
|
||||||
|
defaultMessage : 'Audio device'
|
||||||
|
})}
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
disabled={audioDevices.length === 0 || me.audioInProgress}
|
||||||
|
>
|
||||||
|
{ audioDevices.map((audio, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={audio.deviceId}>{audio.label}</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{ audioDevices.length > 0 ?
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.selectAudio',
|
||||||
|
defaultMessage : 'Select audio device'
|
||||||
|
})
|
||||||
|
:
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.cantSelectAudio',
|
||||||
|
defaultMessage : 'Unable to select audio device'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
{ 'audioOutputSupportedBrowsers' in window.config &&
|
||||||
|
window.config.audioOutputSupportedBrowsers.includes(me.browser.name) &&
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={settings.selectedAudioOutputDevice || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
{
|
||||||
|
if (event.target.value)
|
||||||
|
roomClient.changeAudioOutputDevice(event.target.value);
|
||||||
|
}}
|
||||||
|
displayEmpty
|
||||||
|
name={intl.formatMessage({
|
||||||
|
id : 'settings.audioOutput',
|
||||||
|
defaultMessage : 'Audio output device'
|
||||||
|
})}
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
disabled={audioOutputDevices.length === 0 || me.audioOutputInProgress}
|
||||||
|
>
|
||||||
|
{ audioOutputDevices.map((audioOutput, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={index}
|
||||||
|
value={audioOutput.deviceId}
|
||||||
|
>
|
||||||
|
{audioOutput.label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{ 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'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={settings.resolution || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
{
|
||||||
|
if (event.target.value)
|
||||||
|
roomClient.changeVideoResolution(event.target.value);
|
||||||
|
}}
|
||||||
|
name='Video resolution'
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
>
|
||||||
|
{ resolutions.map((resolution, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={resolution.value}>
|
||||||
|
{resolution.label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
<FormattedMessage
|
||||||
|
id='settings.resolution'
|
||||||
|
defaultMessage='Select your video resolution'
|
||||||
|
/>
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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)));
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
|
||||||
import * as roomActions from '../../actions/roomActions';
|
import * as roomActions from '../../actions/roomActions';
|
||||||
import * as settingsActions from '../../actions/settingsActions';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
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 Dialog from '@material-ui/core/Dialog';
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
|
||||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
const tabs =
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
[
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
'media',
|
||||||
import Select from '@material-ui/core/Select';
|
'appearence',
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
'advanced'
|
||||||
|
];
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -43,106 +46,27 @@ const styles = (theme) =>
|
||||||
width : '90vw'
|
width : '90vw'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setting :
|
tabsHeader :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(2)
|
flexGrow : 1
|
||||||
},
|
|
||||||
formControl :
|
|
||||||
{
|
|
||||||
display : 'flex'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Settings = ({
|
const Settings = ({
|
||||||
roomClient,
|
currentSettingsTab,
|
||||||
room,
|
settingsOpen,
|
||||||
me,
|
|
||||||
settings,
|
|
||||||
onToggleAdvancedMode,
|
|
||||||
onTogglePermanentTopBar,
|
|
||||||
handleCloseSettings,
|
handleCloseSettings,
|
||||||
handleChangeMode,
|
setSettingsTab,
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
const intl = useIntl();
|
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 (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
className={classes.root}
|
className={classes.root}
|
||||||
open={room.settingsOpen}
|
open={settingsOpen}
|
||||||
onClose={() => handleCloseSettings({ settingsOpen: false })}
|
onClose={() => handleCloseSettings(false)}
|
||||||
classes={{
|
classes={{
|
||||||
paper : classes.dialogPaper
|
paper : classes.dialogPaper
|
||||||
}}
|
}}
|
||||||
|
|
@ -153,254 +77,40 @@ const Settings = ({
|
||||||
defaultMessage='Settings'
|
defaultMessage='Settings'
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<form className={classes.setting} autoComplete='off'>
|
<Tabs
|
||||||
<FormControl className={classes.formControl}>
|
className={classes.tabsHeader}
|
||||||
<Select
|
value={tabs.indexOf(currentSettingsTab)}
|
||||||
value={settings.selectedWebcam || ''}
|
onChange={(event, value) => setSettingsTab(tabs[value])}
|
||||||
onChange={(event) =>
|
indicatorColor='primary'
|
||||||
{
|
textColor='primary'
|
||||||
if (event.target.value)
|
variant='fullWidth'
|
||||||
roomClient.changeWebcam(event.target.value);
|
>
|
||||||
}}
|
<Tab
|
||||||
displayEmpty
|
label={
|
||||||
name={intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'settings.camera',
|
id : 'label.media',
|
||||||
defaultMessage : 'Camera'
|
defaultMessage : 'Media'
|
||||||
})}
|
})
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
disabled={webcams.length === 0 || me.webcamInProgress}
|
|
||||||
>
|
|
||||||
{ webcams.map((webcam, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
{ webcams.length > 0 ?
|
|
||||||
intl.formatMessage({
|
|
||||||
id : 'settings.selectCamera',
|
|
||||||
defaultMessage : 'Select video device'
|
|
||||||
})
|
|
||||||
:
|
|
||||||
intl.formatMessage({
|
|
||||||
id : 'settings.cantSelectCamera',
|
|
||||||
defaultMessage : 'Unable to select video device'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
<form className={classes.setting} autoComplete='off'>
|
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={settings.selectedAudioDevice || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
roomClient.changeAudioDevice(event.target.value);
|
|
||||||
}}
|
|
||||||
displayEmpty
|
|
||||||
name={intl.formatMessage({
|
|
||||||
id : 'settings.audio',
|
|
||||||
defaultMessage : 'Audio device'
|
|
||||||
})}
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
disabled={audioDevices.length === 0 || me.audioInProgress}
|
|
||||||
>
|
|
||||||
{ audioDevices.map((audio, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={index} value={audio.deviceId}>{audio.label}</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
{ audioDevices.length > 0 ?
|
|
||||||
intl.formatMessage({
|
|
||||||
id : 'settings.selectAudio',
|
|
||||||
defaultMessage : 'Select audio device'
|
|
||||||
})
|
|
||||||
:
|
|
||||||
intl.formatMessage({
|
|
||||||
id : 'settings.cantSelectAudio',
|
|
||||||
defaultMessage : 'Unable to select audio device'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
{ 'audioOutputSupportedBrowsers' in window.config &&
|
|
||||||
window.config.audioOutputSupportedBrowsers.includes(me.browser.name) &&
|
|
||||||
<form className={classes.setting} autoComplete='off'>
|
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={settings.selectedAudioOutputDevice || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
roomClient.changeAudioOutputDevice(event.target.value);
|
|
||||||
}}
|
|
||||||
displayEmpty
|
|
||||||
name={intl.formatMessage({
|
|
||||||
id : 'settings.audioOutput',
|
|
||||||
defaultMessage : 'Audio output device'
|
|
||||||
})}
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
disabled={audioOutputDevices.length === 0 || me.audioOutputInProgress}
|
|
||||||
>
|
|
||||||
{ audioOutputDevices.map((audioOutput, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
key={index}
|
|
||||||
value={audioOutput.deviceId}
|
|
||||||
>
|
|
||||||
{audioOutput.label}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
{ 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'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
<form className={classes.setting} autoComplete='off'>
|
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={settings.resolution || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
roomClient.changeVideoResolution(event.target.value);
|
|
||||||
}}
|
|
||||||
name='Video resolution'
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
>
|
|
||||||
{ resolutions.map((resolution, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={index} value={resolution.value}>
|
|
||||||
{resolution.label}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.resolution'
|
|
||||||
defaultMessage='Select your video resolution'
|
|
||||||
/>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
<form className={classes.setting} autoComplete='off'>
|
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={room.mode || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
handleChangeMode(event.target.value);
|
|
||||||
}}
|
|
||||||
name={intl.formatMessage({
|
|
||||||
id : 'settings.layout',
|
|
||||||
defaultMessage : 'Room layout'
|
|
||||||
})}
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
>
|
|
||||||
{ modes.map((mode, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={index} value={mode.value}>
|
|
||||||
{mode.label}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.selectRoomLayout'
|
|
||||||
defaultMessage='Select room layout'
|
|
||||||
/>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
<FormControlLabel
|
|
||||||
className={classes.setting}
|
|
||||||
control={<Checkbox checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id : 'settings.advancedMode',
|
|
||||||
defaultMessage : 'Advanced mode'
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
{ settings.advancedMode &&
|
|
||||||
<React.Fragment>
|
|
||||||
{ !window.config.lockLastN &&
|
|
||||||
<form className={classes.setting} autoComplete='off'>
|
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={settings.lastN || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
roomClient.changeMaxSpotlights(event.target.value);
|
|
||||||
}}
|
|
||||||
name='Last N'
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
>
|
|
||||||
{ Array.from(
|
|
||||||
{ length: window.config.maxLastN || 10 },
|
|
||||||
(_, i) => i + 1
|
|
||||||
).map((lastN) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={lastN} value={lastN}>
|
|
||||||
{lastN}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.lastn'
|
|
||||||
defaultMessage='Number of visible videos'
|
|
||||||
/>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
}
|
}
|
||||||
<FormControlLabel
|
/>
|
||||||
className={classes.setting}
|
<Tab
|
||||||
control={<Checkbox checked={settings.permanentTopBar} onChange={onTogglePermanentTopBar} value='permanentTopBar' />}
|
label={intl.formatMessage({
|
||||||
label={intl.formatMessage({
|
id : 'label.appearence',
|
||||||
id : 'settings.permanentTopBar',
|
defaultMessage : 'Appearence'
|
||||||
defaultMessage : 'Permanent top bar'
|
})}
|
||||||
})}
|
/>
|
||||||
/>
|
<Tab
|
||||||
</React.Fragment>
|
label={intl.formatMessage({
|
||||||
}
|
id : 'label.advanced',
|
||||||
|
defaultMessage : 'Advanced'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Tabs>
|
||||||
|
{currentSettingsTab === 'media' && <MediaSettings />}
|
||||||
|
{currentSettingsTab === 'appearence' && <AppearenceSettings />}
|
||||||
|
{currentSettingsTab === 'advanced' && <AdvancedSettings />}
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => handleCloseSettings({ settingsOpen: false })} color='primary'>
|
<Button onClick={() => handleCloseSettings(false)} color='primary'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='label.close'
|
id='label.close'
|
||||||
defaultMessage='Close'
|
defaultMessage='Close'
|
||||||
|
|
@ -413,34 +123,25 @@ const Settings = ({
|
||||||
|
|
||||||
Settings.propTypes =
|
Settings.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
currentSettingsTab : PropTypes.string.isRequired,
|
||||||
me : appPropTypes.Me.isRequired,
|
settingsOpen : PropTypes.bool.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
handleCloseSettings : PropTypes.func.isRequired,
|
||||||
settings : PropTypes.object.isRequired,
|
setSettingsTab : PropTypes.func.isRequired,
|
||||||
onToggleAdvancedMode : PropTypes.func.isRequired,
|
classes : PropTypes.object.isRequired
|
||||||
onTogglePermanentTopBar : PropTypes.func.isRequired,
|
|
||||||
handleChangeMode : PropTypes.func.isRequired,
|
|
||||||
handleCloseSettings : PropTypes.func.isRequired,
|
|
||||||
classes : PropTypes.object.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
({
|
||||||
return {
|
currentSettingsTab : state.room.currentSettingsTab,
|
||||||
me : state.me,
|
settingsOpen : state.room.settingsOpen
|
||||||
room : state.room,
|
});
|
||||||
settings : state.settings
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
onToggleAdvancedMode : settingsActions.toggleAdvancedMode,
|
handleCloseSettings : roomActions.setSettingsOpen,
|
||||||
onTogglePermanentTopBar : settingsActions.togglePermanentTopBar,
|
setSettingsTab : roomActions.setSettingsTab
|
||||||
handleChangeMode : roomActions.setDisplayMode,
|
|
||||||
handleCloseSettings : roomActions.setSettingsOpen
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
null,
|
null,
|
||||||
|
|
@ -448,10 +149,9 @@ export default withRoomContext(connect(
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.me === next.me &&
|
prev.room.currentSettingsTab === next.room.currentSettingsTab &&
|
||||||
prev.room === next.room &&
|
prev.room.settingsOpen === next.room.settingsOpen
|
||||||
prev.settings === next.settings
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)(withStyles(styles)(Settings)));
|
)(withStyles(styles)(Settings));
|
||||||
|
|
@ -96,11 +96,6 @@ const FullScreenView = (props) =>
|
||||||
!consumer.remotelyPaused
|
!consumer.remotelyPaused
|
||||||
);
|
);
|
||||||
|
|
||||||
let consumerProfile;
|
|
||||||
|
|
||||||
if (consumer)
|
|
||||||
consumerProfile = consumer.profile;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.controls}>
|
<div className={classes.controls}>
|
||||||
|
|
@ -121,9 +116,25 @@ const FullScreenView = (props) =>
|
||||||
<VideoView
|
<VideoView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoContain
|
videoContain
|
||||||
videoTrack={consumer ? consumer.track : null}
|
consumerSpatialLayers={consumer ? consumer.spatialLayers : null}
|
||||||
|
consumerTemporalLayers={consumer ? consumer.temporalLayers : null}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
consumer ? consumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
consumer ? consumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
consumer ? consumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
consumer ? consumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={consumer && consumer.type !== 'simple'}
|
||||||
|
videoTrack={consumer && consumer.track}
|
||||||
videoVisible={consumerVisible}
|
videoVisible={consumerVisible}
|
||||||
videoProfile={consumerProfile}
|
videoCodec={consumer && consumer.codec}
|
||||||
|
videoScore={consumer ? consumer.score : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,14 @@ class FullView extends React.PureComponent
|
||||||
this._setTracks(videoTrack);
|
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)
|
_setTracks(videoTrack)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
_setTracks(videoTrack, audioTrack)
|
||||||
|
|
|
||||||
|
|
@ -23,18 +23,29 @@ const VideoWindow = (props) =>
|
||||||
!consumer.remotelyPaused
|
!consumer.remotelyPaused
|
||||||
);
|
);
|
||||||
|
|
||||||
let consumerProfile;
|
|
||||||
|
|
||||||
if (consumer)
|
|
||||||
consumerProfile = consumer.profile;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewWindow onUnload={toggleConsumerWindow}>
|
<NewWindow onUnload={toggleConsumerWindow}>
|
||||||
<FullView
|
<FullView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoTrack={consumer ? consumer.track : null}
|
consumerSpatialLayers={consumer ? consumer.spatialLayers : null}
|
||||||
|
consumerTemporalLayers={consumer ? consumer.temporalLayers : null}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
consumer ? consumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
consumer ? consumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
consumer ? consumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
consumer ? consumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={consumer && consumer.type !== 'simple'}
|
||||||
|
videoTrack={consumer && consumer.track}
|
||||||
videoVisible={consumerVisible}
|
videoVisible={consumerVisible}
|
||||||
videoProfile={consumerProfile}
|
videoCodec={consumer && consumer.codec}
|
||||||
|
videoScore={consumer ? consumer.score : null}
|
||||||
/>
|
/>
|
||||||
</NewWindow>
|
</NewWindow>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ export const Me = PropTypes.shape(
|
||||||
export const Producer = PropTypes.shape(
|
export const Producer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
id : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired,
|
||||||
deviceLabel : PropTypes.string,
|
deviceLabel : PropTypes.string,
|
||||||
type : PropTypes.oneOf([ 'front', 'back', 'screen' ]),
|
type : PropTypes.oneOf([ 'front', 'back', 'screen', 'extravideo' ]),
|
||||||
paused : PropTypes.bool.isRequired,
|
paused : PropTypes.bool.isRequired,
|
||||||
track : PropTypes.any,
|
track : PropTypes.any,
|
||||||
codec : PropTypes.string.isRequired
|
codec : PropTypes.string.isRequired
|
||||||
|
|
@ -37,7 +37,7 @@ export const Consumer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
id : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
peerId : 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,
|
locallyPaused : PropTypes.bool.isRequired,
|
||||||
remotelyPaused : PropTypes.bool.isRequired,
|
remotelyPaused : PropTypes.bool.isRequired,
|
||||||
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.1"
|
|
||||||
id="Layer_1"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 96 96"
|
|
||||||
style="enable-background:new 0 0 96 96;"
|
|
||||||
xml:space="preserve">
|
|
||||||
<metadata
|
|
||||||
id="metadata11"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
|
|
||||||
<defs
|
|
||||||
id="defs9" />
|
|
||||||
<path
|
|
||||||
style="fill:#000000;stroke-width:0.40677965"
|
|
||||||
d="m 33.894283,77.837288 c -1.428534,-1.845763 -3.909722,-5.220659 -5.513751,-7.499764 -1.60403,-2.279109 -4.323663,-5.940126 -6.043631,-8.135593 -5.698554,-7.273973 -6.224902,-8.044795 -6.226676,-9.118803 -0.0034,-2.075799 2.81181,-4.035355 4.9813,-3.467247 0.50339,0.131819 2.562712,1.72771 4.576272,3.546423 4.238418,3.828283 6.617166,5.658035 7.355654,5.658035 0.82497,0 1.045415,-1.364294 0.567453,-3.511881 C 33.348583,54.219654 31.1088,48.20339 28.613609,41.938983 23.524682,29.162764 23.215312,27.731034 25.178629,26.04226 c 2.443255,-2.101599 4.670178,-1.796504 6.362271,0.87165 0.639176,1.007875 2.666245,5.291978 4.504599,9.520229 1.838354,4.228251 3.773553,8.092718 4.300442,8.587705 l 0.957981,0.899977 0.419226,-1.102646 c 0.255274,-0.671424 0.419225,-6.068014 0.419225,-13.799213 0,-13.896836 -0.0078,-13.84873 2.44517,-15.1172 1.970941,-1.019214 4.2259,-0.789449 5.584354,0.569005 l 1.176852,1.176852 0.483523,11.738402 c 0.490017,11.896027 0.826095,14.522982 1.911266,14.939402 1.906224,0.731486 2.21601,-0.184677 4.465407,-13.206045 1.239206,-7.173539 1.968244,-10.420721 2.462128,-10.966454 1.391158,-1.537215 4.742705,-1.519809 6.295208,0.03269 1.147387,1.147388 1.05469,3.124973 -0.669503,14.283063 -0.818745,5.298489 -1.36667,10.090163 -1.220432,10.67282 0.14596,0.581557 0.724796,1.358395 1.286298,1.726306 0.957759,0.627548 1.073422,0.621575 1.86971,-0.09655 0.466837,-0.421011 1.761787,-2.595985 2.877665,-4.833273 2.564176,-5.141059 3.988466,-6.711864 6.085822,-6.711864 2.769954,0 3.610947,2.927256 2.139316,7.446329 C 78.799497,44.318351 66.752066,77.28024 65.51653,80.481356 65.262041,81.140709 64.18139,81.19322 50.866695,81.19322 H 36.491617 Z"
|
|
||||||
id="path3710"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.1"
|
|
||||||
id="Layer_1"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 96 96"
|
|
||||||
style="enable-background:new 0 0 96 96;"
|
|
||||||
xml:space="preserve">
|
|
||||||
<metadata
|
|
||||||
id="metadata11"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
|
|
||||||
<defs
|
|
||||||
id="defs9" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;stroke-width:0.40677965"
|
|
||||||
d="m 33.894283,77.837288 c -1.428534,-1.845763 -3.909722,-5.220659 -5.513751,-7.499764 -1.60403,-2.279109 -4.323663,-5.940126 -6.043631,-8.135593 -5.698554,-7.273973 -6.224902,-8.044795 -6.226676,-9.118803 -0.0034,-2.075799 2.81181,-4.035355 4.9813,-3.467247 0.50339,0.131819 2.562712,1.72771 4.576272,3.546423 4.238418,3.828283 6.617166,5.658035 7.355654,5.658035 0.82497,0 1.045415,-1.364294 0.567453,-3.511881 C 33.348583,54.219654 31.1088,48.20339 28.613609,41.938983 23.524682,29.162764 23.215312,27.731034 25.178629,26.04226 c 2.443255,-2.101599 4.670178,-1.796504 6.362271,0.87165 0.639176,1.007875 2.666245,5.291978 4.504599,9.520229 1.838354,4.228251 3.773553,8.092718 4.300442,8.587705 l 0.957981,0.899977 0.419226,-1.102646 c 0.255274,-0.671424 0.419225,-6.068014 0.419225,-13.799213 0,-13.896836 -0.0078,-13.84873 2.44517,-15.1172 1.970941,-1.019214 4.2259,-0.789449 5.584354,0.569005 l 1.176852,1.176852 0.483523,11.738402 c 0.490017,11.896027 0.826095,14.522982 1.911266,14.939402 1.906224,0.731486 2.21601,-0.184677 4.465407,-13.206045 1.239206,-7.173539 1.968244,-10.420721 2.462128,-10.966454 1.391158,-1.537215 4.742705,-1.519809 6.295208,0.03269 1.147387,1.147388 1.05469,3.124973 -0.669503,14.283063 -0.818745,5.298489 -1.36667,10.090163 -1.220432,10.67282 0.14596,0.581557 0.724796,1.358395 1.286298,1.726306 0.957759,0.627548 1.073422,0.621575 1.86971,-0.09655 0.466837,-0.421011 1.761787,-2.595985 2.877665,-4.833273 2.564176,-5.141059 3.988466,-6.711864 6.085822,-6.711864 2.769954,0 3.610947,2.927256 2.139316,7.446329 C 78.799497,44.318351 66.752066,77.28024 65.51653,80.481356 65.262041,81.140709 64.18139,81.19322 50.866695,81.19322 H 36.491617 Z"
|
|
||||||
id="path3710"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
|
@ -15,8 +15,8 @@ const initialState =
|
||||||
screenShareInProgress : false,
|
screenShareInProgress : false,
|
||||||
displayNameInProgress : false,
|
displayNameInProgress : false,
|
||||||
loginEnabled : false,
|
loginEnabled : false,
|
||||||
raiseHand : false,
|
raisedHand : false,
|
||||||
raiseHandInProgress : false,
|
raisedHandInProgress : false,
|
||||||
loggedIn : false,
|
loggedIn : false,
|
||||||
isSpeaking : false
|
isSpeaking : false
|
||||||
};
|
};
|
||||||
|
|
@ -134,18 +134,18 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, screenShareInProgress: flag };
|
return { ...state, screenShareInProgress: flag };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_MY_RAISE_HAND_STATE':
|
case 'SET_RAISED_HAND':
|
||||||
{
|
{
|
||||||
const { flag } = action.payload;
|
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;
|
const { flag } = action.payload;
|
||||||
|
|
||||||
return { ...state, raiseHandInProgress: flag };
|
return { ...state, raisedHandInProgress: flag };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_DISPLAY_NAME_IN_PROGRESS':
|
case 'SET_DISPLAY_NAME_IN_PROGRESS':
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ const peer = (state = {}, action) =>
|
||||||
case 'SET_PEER_KICK_IN_PROGRESS':
|
case 'SET_PEER_KICK_IN_PROGRESS':
|
||||||
return { ...state, peerKickInProgress: action.payload.flag };
|
return { ...state, peerKickInProgress: action.payload.flag };
|
||||||
|
|
||||||
case 'SET_PEER_RAISE_HAND_STATE':
|
case 'SET_PEER_RAISED_HAND':
|
||||||
return { ...state, raiseHandState: action.payload.raiseHandState };
|
return { ...state, raisedHand: action.payload.raisedHand };
|
||||||
|
|
||||||
case 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
{
|
{
|
||||||
|
|
@ -86,7 +86,7 @@ const peers = (state = {}, action) =>
|
||||||
case 'SET_PEER_VIDEO_IN_PROGRESS':
|
case 'SET_PEER_VIDEO_IN_PROGRESS':
|
||||||
case 'SET_PEER_AUDIO_IN_PROGRESS':
|
case 'SET_PEER_AUDIO_IN_PROGRESS':
|
||||||
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
||||||
case 'SET_PEER_RAISE_HAND_STATE':
|
case 'SET_PEER_RAISED_HAND':
|
||||||
case 'SET_PEER_PICTURE':
|
case 'SET_PEER_PICTURE':
|
||||||
case 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
case 'ADD_PEER_ROLE':
|
case 'ADD_PEER_ROLE':
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ const initialState =
|
||||||
selectedPeerId : null,
|
selectedPeerId : null,
|
||||||
spotlights : [],
|
spotlights : [],
|
||||||
settingsOpen : false,
|
settingsOpen : false,
|
||||||
|
extraVideoOpen : false,
|
||||||
|
currentSettingsTab : 'media', // media, appearence, advanced
|
||||||
lockDialogOpen : false,
|
lockDialogOpen : false,
|
||||||
joined : false,
|
joined : false,
|
||||||
muteAllInProgress : false,
|
muteAllInProgress : false,
|
||||||
|
|
@ -34,6 +36,7 @@ const initialState =
|
||||||
SEND_CHAT : [],
|
SEND_CHAT : [],
|
||||||
MODERATE_CHAT : [],
|
MODERATE_CHAT : [],
|
||||||
SHARE_SCREEN : [],
|
SHARE_SCREEN : [],
|
||||||
|
EXTRA_VIDEO : [],
|
||||||
SHARE_FILE : [],
|
SHARE_FILE : [],
|
||||||
MODERATE_FILES : [],
|
MODERATE_FILES : [],
|
||||||
MODERATE_ROOM : []
|
MODERATE_ROOM : []
|
||||||
|
|
@ -113,6 +116,20 @@ const room = (state = initialState, action) =>
|
||||||
return { ...state, settingsOpen };
|
return { ...state, settingsOpen };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_EXTRA_VIDEO_OPEN':
|
||||||
|
{
|
||||||
|
const { extraVideoOpen } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, extraVideoOpen };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_SETTINGS_TAB':
|
||||||
|
{
|
||||||
|
const { tab } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, currentSettingsTab: tab };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_ROOM_ACTIVE_SPEAKER':
|
case 'SET_ROOM_ACTIVE_SPEAKER':
|
||||||
{
|
{
|
||||||
const { peerId } = action.payload;
|
const { peerId } = action.payload;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ const initialState =
|
||||||
// low, medium, high, veryhigh, ultra
|
// low, medium, high, veryhigh, ultra
|
||||||
resolution : window.config.defaultResolution || 'medium',
|
resolution : window.config.defaultResolution || 'medium',
|
||||||
lastN : 4,
|
lastN : 4,
|
||||||
permanentTopBar : true
|
permanentTopBar : true,
|
||||||
|
hiddenControls : false,
|
||||||
|
notificationSounds : true
|
||||||
};
|
};
|
||||||
|
|
||||||
const settings = (state = initialState, action) =>
|
const settings = (state = initialState, action) =>
|
||||||
|
|
@ -57,6 +59,20 @@ const settings = (state = initialState, action) =>
|
||||||
return { ...state, permanentTopBar };
|
return { ...state, permanentTopBar };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'TOGGLE_HIDDEN_CONTROLS':
|
||||||
|
{
|
||||||
|
const hiddenControls = !state.hiddenControls;
|
||||||
|
|
||||||
|
return { ...state, hiddenControls };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'TOGGLE_NOTIFICATION_SOUNDS':
|
||||||
|
{
|
||||||
|
const notificationSounds = !state.notificationSounds;
|
||||||
|
|
||||||
|
return { ...state, notificationSounds };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_VIDEO_RESOLUTION':
|
case 'SET_VIDEO_RESOLUTION':
|
||||||
{
|
{
|
||||||
const { resolution } = action.payload;
|
const { resolution } = action.payload;
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "登录",
|
"tooltip.login": "登录",
|
||||||
"tooltip.logout": "注销",
|
"tooltip.logout": "注销",
|
||||||
"tooltip.admitFromLobby": "从大厅允许",
|
"tooltip.admitFromLobby": "从大厅允许",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "房间名称",
|
"label.roomName": "房间名称",
|
||||||
"label.chooseRoomButton": "继续",
|
"label.chooseRoomButton": "继续",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "非常高 (FHD)",
|
"label.veryHigh": "非常高 (FHD)",
|
||||||
"label.ultra": "超高 (UHD)",
|
"label.ultra": "超高 (UHD)",
|
||||||
"label.close": "关闭",
|
"label.close": "关闭",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "设置",
|
"settings.settings": "设置",
|
||||||
"settings.camera": "视频设备",
|
"settings.camera": "视频设备",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "高级模式",
|
"settings.advancedMode": "高级模式",
|
||||||
"settings.permanentTopBar": "永久顶吧",
|
"settings.permanentTopBar": "永久顶吧",
|
||||||
"settings.lastn": "可见视频数量",
|
"settings.lastn": "可见视频数量",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "无法保存文件",
|
"filesharing.saveFileError": "无法保存文件",
|
||||||
"filesharing.startingFileShare": "正在尝试共享文件",
|
"filesharing.startingFileShare": "正在尝试共享文件",
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Přihlášení",
|
"tooltip.login": "Přihlášení",
|
||||||
"tooltip.logout": "Odhlášení",
|
"tooltip.logout": "Odhlášení",
|
||||||
"tooltip.admitFromLobby": "Povolit uživatele z Přijímací místnosti",
|
"tooltip.admitFromLobby": "Povolit uživatele z Přijímací místnosti",
|
||||||
|
|
@ -71,6 +77,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Jméno místnosti",
|
"label.roomName": "Jméno místnosti",
|
||||||
"label.chooseRoomButton": "Pokračovat",
|
"label.chooseRoomButton": "Pokračovat",
|
||||||
|
|
@ -94,6 +101,10 @@
|
||||||
"label.veryHigh": "Velmi vysoké (FHD)",
|
"label.veryHigh": "Velmi vysoké (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Zavřít",
|
"label.close": "Zavřít",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Nastavení",
|
"settings.settings": "Nastavení",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -109,6 +120,10 @@
|
||||||
"settings.layout": "Rozvržení místnosti",
|
"settings.layout": "Rozvržení místnosti",
|
||||||
"settings.selectRoomLayout": "Vyberte rozvržení místnosti",
|
"settings.selectRoomLayout": "Vyberte rozvržení místnosti",
|
||||||
"settings.advancedMode": "Pokočilý mód",
|
"settings.advancedMode": "Pokočilý mód",
|
||||||
|
"settings.permanentTopBar": null,
|
||||||
|
"settings.lastn": null,
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Není možné uložit soubor",
|
"filesharing.saveFileError": "Není možné uložit soubor",
|
||||||
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
|
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,14 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": "Dein Browser unterstützt keine Spracherkennung",
|
"room.speechUnsupported": "Dein Browser unterstützt keine Spracherkennung",
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": 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.login": "Anmelden",
|
||||||
"tooltip.logout": "Abmelden",
|
"tooltip.logout": "Abmelden",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": "Teilnehmer rauswerfen",
|
"tooltip.kickParticipant": "Teilnehmer rauswerfen",
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Name des Raums",
|
"label.roomName": "Name des Raums",
|
||||||
"label.chooseRoomButton": "Weiter",
|
"label.chooseRoomButton": "Weiter",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Sehr hoch (FHD)",
|
"label.veryHigh": "Sehr hoch (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Schließen",
|
"label.close": "Schließen",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Einstellungen",
|
"settings.settings": "Einstellungen",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Erweiterter Modus",
|
"settings.advancedMode": "Erweiterter Modus",
|
||||||
"settings.permanentTopBar": "Permanente obere Leiste",
|
"settings.permanentTopBar": "Permanente obere Leiste",
|
||||||
"settings.lastn": "Anzahl der sichtbaren Videos",
|
"settings.lastn": "Anzahl der sichtbaren Videos",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Fehler beim Speichern der Datei",
|
"filesharing.saveFileError": "Fehler beim Speichern der Datei",
|
||||||
"filesharing.startingFileShare": "Starte Teilen der Datei",
|
"filesharing.startingFileShare": "Starte Teilen der Datei",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Log ind",
|
"tooltip.login": "Log ind",
|
||||||
"tooltip.logout": "Log ud",
|
"tooltip.logout": "Log ud",
|
||||||
"tooltip.admitFromLobby": "Giv adgang fra lobbyen",
|
"tooltip.admitFromLobby": "Giv adgang fra lobbyen",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Værelsesnavn",
|
"label.roomName": "Værelsesnavn",
|
||||||
"label.chooseRoomButton": "Fortsæt",
|
"label.chooseRoomButton": "Fortsæt",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Meget høj (FHD)",
|
"label.veryHigh": "Meget høj (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Luk",
|
"label.close": "Luk",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Indstillinger",
|
"settings.settings": "Indstillinger",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -106,13 +117,14 @@
|
||||||
"settings.audioOutput": "Audio output enhed",
|
"settings.audioOutput": "Audio output enhed",
|
||||||
"settings.selectAudioOutput": "Vælg lydudgangsenhed",
|
"settings.selectAudioOutput": "Vælg lydudgangsenhed",
|
||||||
"settings.cantSelectAudioOutput": "Kan ikke vælge lydoutputenhed",
|
"settings.cantSelectAudioOutput": "Kan ikke vælge lydoutputenhed",
|
||||||
|
|
||||||
"settings.resolution": "Vælg din videoopløsning",
|
"settings.resolution": "Vælg din videoopløsning",
|
||||||
"settings.layout": "Møde visning",
|
"settings.layout": "Møde visning",
|
||||||
"settings.selectRoomLayout": "Vælg møde visning",
|
"settings.selectRoomLayout": "Vælg møde visning",
|
||||||
"settings.advancedMode": "Avanceret tilstand",
|
"settings.advancedMode": "Avanceret tilstand",
|
||||||
"settings.permanentTopBar": "Permanent øverste linje",
|
"settings.permanentTopBar": "Permanent øverste linje",
|
||||||
"settings.lastn": "Antal synlige videoer",
|
"settings.lastn": "Antal synlige videoer",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Kan ikke gemme fil",
|
"filesharing.saveFileError": "Kan ikke gemme fil",
|
||||||
"filesharing.startingFileShare": "Forsøger at dele filen",
|
"filesharing.startingFileShare": "Forsøger at dele filen",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Σύνδεση",
|
"tooltip.login": "Σύνδεση",
|
||||||
"tooltip.logout": "Αποσύνδεση",
|
"tooltip.logout": "Αποσύνδεση",
|
||||||
"tooltip.admitFromLobby": "Admit from lobby",
|
"tooltip.admitFromLobby": "Admit from lobby",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Όνομα δωματίου",
|
"label.roomName": "Όνομα δωματίου",
|
||||||
"label.chooseRoomButton": "Συνέχεια",
|
"label.chooseRoomButton": "Συνέχεια",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Πολύ υψηλή (FHD)",
|
"label.veryHigh": "Πολύ υψηλή (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Κλείσιμο",
|
"label.close": "Κλείσιμο",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Ρυθμίσεις",
|
"settings.settings": "Ρυθμίσεις",
|
||||||
"settings.camera": "Κάμερα",
|
"settings.camera": "Κάμερα",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Προηγμένη λειτουργία",
|
"settings.advancedMode": "Προηγμένη λειτουργία",
|
||||||
"settings.permanentTopBar": "Μόνιμη μπάρα κορυφής",
|
"settings.permanentTopBar": "Μόνιμη μπάρα κορυφής",
|
||||||
"settings.lastn": "Αριθμός ορατών βίντεο",
|
"settings.lastn": "Αριθμός ορατών βίντεο",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Αδυναμία αποθήκευσης του αρχείου",
|
"filesharing.saveFileError": "Αδυναμία αποθήκευσης του αρχείου",
|
||||||
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
|
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": "Clear files",
|
"room.clearFileSharing": "Clear files",
|
||||||
"room.speechUnsupported": "Your browser does not support speech recognition",
|
"room.speechUnsupported": "Your browser does not support speech recognition",
|
||||||
"room.moderatoractions": "Moderator actions",
|
"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",
|
"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.login": "Log in",
|
||||||
"tooltip.logout": "Log out",
|
"tooltip.logout": "Log out",
|
||||||
"tooltip.admitFromLobby": "Admit from lobby",
|
"tooltip.admitFromLobby": "Admit from lobby",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": "Kick out participant",
|
"tooltip.kickParticipant": "Kick out participant",
|
||||||
"tooltip.muteParticipant": "Mute participant",
|
"tooltip.muteParticipant": "Mute participant",
|
||||||
"tooltip.muteParticipantVideo": "Mute participant video",
|
"tooltip.muteParticipantVideo": "Mute participant video",
|
||||||
|
"tooltip.raisedHand": "Raise hand",
|
||||||
|
|
||||||
"label.roomName": "Room name",
|
"label.roomName": "Room name",
|
||||||
"label.chooseRoomButton": "Continue",
|
"label.chooseRoomButton": "Continue",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Very high (FHD)",
|
"label.veryHigh": "Very high (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Close",
|
"label.close": "Close",
|
||||||
|
"label.media": "Media",
|
||||||
|
"label.appearence": "Appearence",
|
||||||
|
"label.advanced": "Advanced",
|
||||||
|
"label.addVideo": "Add video",
|
||||||
|
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"settings.camera": "Camera",
|
"settings.camera": "Camera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Advanced mode",
|
"settings.advancedMode": "Advanced mode",
|
||||||
"settings.permanentTopBar": "Permanent top bar",
|
"settings.permanentTopBar": "Permanent top bar",
|
||||||
"settings.lastn": "Number of visible videos",
|
"settings.lastn": "Number of visible videos",
|
||||||
|
"settings.hiddenControls": "Hidden media controls",
|
||||||
|
"settings.notificationSounds": "Notification sounds",
|
||||||
|
|
||||||
"filesharing.saveFileError": "Unable to save file",
|
"filesharing.saveFileError": "Unable to save file",
|
||||||
"filesharing.startingFileShare": "Attempting to share file",
|
"filesharing.startingFileShare": "Attempting to share file",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Entrar",
|
"tooltip.login": "Entrar",
|
||||||
"tooltip.logout": "Salir",
|
"tooltip.logout": "Salir",
|
||||||
"tooltip.admitFromLobby": "Admitir desde la sala de espera",
|
"tooltip.admitFromLobby": "Admitir desde la sala de espera",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Nombre de la sala",
|
"label.roomName": "Nombre de la sala",
|
||||||
"label.chooseRoomButton": "Continuar",
|
"label.chooseRoomButton": "Continuar",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Muy alta (FHD)",
|
"label.veryHigh": "Muy alta (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Cerrar",
|
"label.close": "Cerrar",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Ajustes",
|
"settings.settings": "Ajustes",
|
||||||
"settings.camera": "Cámara",
|
"settings.camera": "Cámara",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Modo avanzado",
|
"settings.advancedMode": "Modo avanzado",
|
||||||
"settings.permanentTopBar": "Barra superior permanente",
|
"settings.permanentTopBar": "Barra superior permanente",
|
||||||
"settings.lastn": "Cantidad de videos visibles",
|
"settings.lastn": "Cantidad de videos visibles",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
||||||
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Connexion",
|
"tooltip.login": "Connexion",
|
||||||
"tooltip.logout": "Déconnexion",
|
"tooltip.logout": "Déconnexion",
|
||||||
"tooltip.admitFromLobby": "Autorisé depuis la salle d'attente",
|
"tooltip.admitFromLobby": "Autorisé depuis la salle d'attente",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Nom de la salle",
|
"label.roomName": "Nom de la salle",
|
||||||
"label.chooseRoomButton": "Continuer",
|
"label.chooseRoomButton": "Continuer",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Très Haute Définition (FHD)",
|
"label.veryHigh": "Très Haute Définition (FHD)",
|
||||||
"label.ultra": "Ultra Haute Définition",
|
"label.ultra": "Ultra Haute Définition",
|
||||||
"label.close": "Fermer",
|
"label.close": "Fermer",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Paramètres",
|
"settings.settings": "Paramètres",
|
||||||
"settings.camera": "Caméra",
|
"settings.camera": "Caméra",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Mode avancé",
|
"settings.advancedMode": "Mode avancé",
|
||||||
"settings.permanentTopBar": "Barre supérieure permanente",
|
"settings.permanentTopBar": "Barre supérieure permanente",
|
||||||
"settings.lastn": "Nombre de vidéos visibles",
|
"settings.lastn": "Nombre de vidéos visibles",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Impossible d'enregistrer le fichier",
|
"filesharing.saveFileError": "Impossible d'enregistrer le fichier",
|
||||||
"filesharing.startingFileShare": "Début du transfert de fichier",
|
"filesharing.startingFileShare": "Début du transfert de fichier",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": "Vaš preglednik ne podržava prepoznavanje govora",
|
"room.speechUnsupported": "Vaš preglednik ne podržava prepoznavanje govora",
|
||||||
"room.moderatoractions": null,
|
"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",
|
"me.mutedPTT": "Utišani ste, pritisnite i držite SPACE tipku za razgovor",
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Prijava",
|
"tooltip.login": "Prijava",
|
||||||
"tooltip.logout": "Odjava",
|
"tooltip.logout": "Odjava",
|
||||||
"tooltip.admitFromLobby": "Pusti iz predvorja",
|
"tooltip.admitFromLobby": "Pusti iz predvorja",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": "Izbaci sudionika",
|
"tooltip.kickParticipant": "Izbaci sudionika",
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Naziv sobe",
|
"label.roomName": "Naziv sobe",
|
||||||
"label.chooseRoomButton": "Nastavi",
|
"label.chooseRoomButton": "Nastavi",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Vrlo visoka (FHD)",
|
"label.veryHigh": "Vrlo visoka (FHD)",
|
||||||
"label.ultra": "Ultra visoka (UHD)",
|
"label.ultra": "Ultra visoka (UHD)",
|
||||||
"label.close": "Zatvori",
|
"label.close": "Zatvori",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Postavke",
|
"settings.settings": "Postavke",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Napredne mogućnosti",
|
"settings.advancedMode": "Napredne mogućnosti",
|
||||||
"settings.permanentTopBar": "Stalna gornja šipka",
|
"settings.permanentTopBar": "Stalna gornja šipka",
|
||||||
"settings.lastn": "Broj vidljivih videozapisa",
|
"settings.lastn": "Broj vidljivih videozapisa",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Nije moguće spremiti datoteku",
|
"filesharing.saveFileError": "Nije moguće spremiti datoteku",
|
||||||
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
|
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Belépés",
|
"tooltip.login": "Belépés",
|
||||||
"tooltip.logout": "Kilépés",
|
"tooltip.logout": "Kilépés",
|
||||||
"tooltip.admitFromLobby": "Beenegdem az előszobából",
|
"tooltip.admitFromLobby": "Beenegdem az előszobából",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Konferencia",
|
"label.roomName": "Konferencia",
|
||||||
"label.chooseRoomButton": "Tovább",
|
"label.chooseRoomButton": "Tovább",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Nagyon magas (FHD)",
|
"label.veryHigh": "Nagyon magas (FHD)",
|
||||||
"label.ultra": "Ultra magas (UHD)",
|
"label.ultra": "Ultra magas (UHD)",
|
||||||
"label.close": "Bezár",
|
"label.close": "Bezár",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Beállítások",
|
"settings.settings": "Beállítások",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Részletes információk",
|
"settings.advancedMode": "Részletes információk",
|
||||||
"settings.permanentTopBar": "Állandó felső sáv",
|
"settings.permanentTopBar": "Állandó felső sáv",
|
||||||
"settings.lastn": "A látható videók száma",
|
"settings.lastn": "A látható videók száma",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "A file-t nem sikerült elmenteni",
|
"filesharing.saveFileError": "A file-t nem sikerült elmenteni",
|
||||||
"filesharing.startingFileShare": "Fájl megosztása",
|
"filesharing.startingFileShare": "Fájl megosztása",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Log in",
|
"tooltip.login": "Log in",
|
||||||
"tooltip.logout": "Log out",
|
"tooltip.logout": "Log out",
|
||||||
"tooltip.admitFromLobby": "Ammetti dalla lobby",
|
"tooltip.admitFromLobby": "Ammetti dalla lobby",
|
||||||
|
|
@ -71,6 +77,7 @@
|
||||||
"tooltip.participants": "Mostra partecipanti",
|
"tooltip.participants": "Mostra partecipanti",
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Nome della stanza",
|
"label.roomName": "Nome della stanza",
|
||||||
"label.chooseRoomButton": "Continua",
|
"label.chooseRoomButton": "Continua",
|
||||||
|
|
@ -94,6 +101,10 @@
|
||||||
"label.veryHigh": "Molto alta (FHD)",
|
"label.veryHigh": "Molto alta (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Chiudi",
|
"label.close": "Chiudi",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Impostazioni",
|
"settings.settings": "Impostazioni",
|
||||||
"settings.camera": "Videocamera",
|
"settings.camera": "Videocamera",
|
||||||
|
|
@ -111,6 +122,8 @@
|
||||||
"settings.advancedMode": "Modalità avanzata",
|
"settings.advancedMode": "Modalità avanzata",
|
||||||
"settings.permanentTopBar": "Barra superiore permanente",
|
"settings.permanentTopBar": "Barra superiore permanente",
|
||||||
"settings.lastn": "Numero di video visibili",
|
"settings.lastn": "Numero di video visibili",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Impossibile salvare file",
|
"filesharing.saveFileError": "Impossibile salvare file",
|
||||||
"filesharing.startingFileShare": "Tentativo di condivisione file",
|
"filesharing.startingFileShare": "Tentativo di condivisione file",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": "Fjern filer",
|
"room.clearFileSharing": "Fjern filer",
|
||||||
"room.speechUnsupported": "Din nettleser støtter ikke stemmegjenkjenning",
|
"room.speechUnsupported": "Din nettleser støtter ikke stemmegjenkjenning",
|
||||||
"room.moderatoractions": "Moderatorhandlinger",
|
"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",
|
"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.login": "Logg in",
|
||||||
"tooltip.logout": "Logg ut",
|
"tooltip.logout": "Logg ut",
|
||||||
"tooltip.admitFromLobby": "Slipp inn fra lobby",
|
"tooltip.admitFromLobby": "Slipp inn fra lobby",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": "Spark ut deltaker",
|
"tooltip.kickParticipant": "Spark ut deltaker",
|
||||||
"tooltip.muteParticipant": "Demp deltaker",
|
"tooltip.muteParticipant": "Demp deltaker",
|
||||||
"tooltip.muteParticipantVideo": "Demp deltakervideo",
|
"tooltip.muteParticipantVideo": "Demp deltakervideo",
|
||||||
|
"tooltip.raisedHand": "Rekk opp hånden",
|
||||||
|
|
||||||
"label.roomName": "Møtenavn",
|
"label.roomName": "Møtenavn",
|
||||||
"label.chooseRoomButton": "Fortsett",
|
"label.chooseRoomButton": "Fortsett",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Veldig høy (FHD)",
|
"label.veryHigh": "Veldig høy (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Lukk",
|
"label.close": "Lukk",
|
||||||
|
"label.media": "Media",
|
||||||
|
"label.appearence": "Utseende",
|
||||||
|
"label.advanced": "Avansert",
|
||||||
|
"label.addVideo": "Legg til video",
|
||||||
|
|
||||||
"settings.settings": "Innstillinger",
|
"settings.settings": "Innstillinger",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Avansert modus",
|
"settings.advancedMode": "Avansert modus",
|
||||||
"settings.permanentTopBar": "Permanent topplinje",
|
"settings.permanentTopBar": "Permanent topplinje",
|
||||||
"settings.lastn": "Antall videoer synlig",
|
"settings.lastn": "Antall videoer synlig",
|
||||||
|
"settings.hiddenControls": "Skjul media knapper",
|
||||||
|
"settings.notificationSounds": "Varslingslyder",
|
||||||
|
|
||||||
"filesharing.saveFileError": "Klarte ikke å lagre fil",
|
"filesharing.saveFileError": "Klarte ikke å lagre fil",
|
||||||
"filesharing.startingFileShare": "Starter fildeling",
|
"filesharing.startingFileShare": "Starter fildeling",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Zaloguj",
|
"tooltip.login": "Zaloguj",
|
||||||
"tooltip.logout": "Wyloguj",
|
"tooltip.logout": "Wyloguj",
|
||||||
"tooltip.admitFromLobby": "Przejście z poczekalni",
|
"tooltip.admitFromLobby": "Przejście z poczekalni",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Nazwa konferencji",
|
"label.roomName": "Nazwa konferencji",
|
||||||
"label.chooseRoomButton": "Kontynuuj",
|
"label.chooseRoomButton": "Kontynuuj",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Bardzo wysoka (FHD)",
|
"label.veryHigh": "Bardzo wysoka (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Zamknij",
|
"label.close": "Zamknij",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Ustawienia",
|
"settings.settings": "Ustawienia",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Tryb zaawansowany",
|
"settings.advancedMode": "Tryb zaawansowany",
|
||||||
"settings.permanentTopBar": "Stały górny pasek",
|
"settings.permanentTopBar": "Stały górny pasek",
|
||||||
"settings.lastn": "Liczba widocznych uczestników (zdalnych)",
|
"settings.lastn": "Liczba widocznych uczestników (zdalnych)",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Nie można zapisać pliku",
|
"filesharing.saveFileError": "Nie można zapisać pliku",
|
||||||
"filesharing.startingFileShare": "Próba udostępnienia pliku",
|
"filesharing.startingFileShare": "Próba udostępnienia pliku",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Entrar",
|
"tooltip.login": "Entrar",
|
||||||
"tooltip.logout": "Sair",
|
"tooltip.logout": "Sair",
|
||||||
"tooltip.admitFromLobby": "Admitir da sala de espera",
|
"tooltip.admitFromLobby": "Admitir da sala de espera",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Nome da sala",
|
"label.roomName": "Nome da sala",
|
||||||
"label.chooseRoomButton": "Continuar",
|
"label.chooseRoomButton": "Continuar",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Muito alta (FHD)",
|
"label.veryHigh": "Muito alta (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Fechar",
|
"label.close": "Fechar",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Definições",
|
"settings.settings": "Definições",
|
||||||
"settings.camera": "Camera",
|
"settings.camera": "Camera",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Modo avançado",
|
"settings.advancedMode": "Modo avançado",
|
||||||
"settings.permanentTopBar": "Barra superior permanente",
|
"settings.permanentTopBar": "Barra superior permanente",
|
||||||
"settings.lastn": "Número de vídeos visíveis",
|
"settings.lastn": "Número de vídeos visíveis",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Impossível de gravar o ficheiro",
|
"filesharing.saveFileError": "Impossível de gravar o ficheiro",
|
||||||
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
|
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
"me.mutedPTT": null,
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Intră în cont",
|
"tooltip.login": "Intră în cont",
|
||||||
"tooltip.logout": "Deconectare",
|
"tooltip.logout": "Deconectare",
|
||||||
"tooltip.admitFromLobby": "Admite din hol",
|
"tooltip.admitFromLobby": "Admite din hol",
|
||||||
|
|
@ -72,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Numele camerei",
|
"label.roomName": "Numele camerei",
|
||||||
"label.chooseRoomButton": "Continuare",
|
"label.chooseRoomButton": "Continuare",
|
||||||
|
|
@ -95,6 +102,10 @@
|
||||||
"label.veryHigh": "Rezoluție foarte înaltă (FHD)",
|
"label.veryHigh": "Rezoluție foarte înaltă (FHD)",
|
||||||
"label.ultra": "Rezoluție ultra înaltă (UHD)",
|
"label.ultra": "Rezoluție ultra înaltă (UHD)",
|
||||||
"label.close": "Închide",
|
"label.close": "Închide",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Setări",
|
"settings.settings": "Setări",
|
||||||
"settings.camera": "Cameră video",
|
"settings.camera": "Cameră video",
|
||||||
|
|
@ -112,6 +123,8 @@
|
||||||
"settings.advancedMode": "Mod avansat",
|
"settings.advancedMode": "Mod avansat",
|
||||||
"settings.permanentTopBar": "Bara de sus permanentă",
|
"settings.permanentTopBar": "Bara de sus permanentă",
|
||||||
"settings.lastn": "Numărul de videoclipuri vizibile",
|
"settings.lastn": "Numărul de videoclipuri vizibile",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Încercarea de a salva fișierul a eșuat",
|
"filesharing.saveFileError": "Încercarea de a salva fișierul a eșuat",
|
||||||
"filesharing.startingFileShare": "Partajarea fișierului",
|
"filesharing.startingFileShare": "Partajarea fișierului",
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,14 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Giriş",
|
"tooltip.login": "Giriş",
|
||||||
"tooltip.logout": "Çıkış",
|
"tooltip.logout": "Çıkış",
|
||||||
|
|
@ -70,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Oda adı",
|
"label.roomName": "Oda adı",
|
||||||
"label.chooseRoomButton": "Devam",
|
"label.chooseRoomButton": "Devam",
|
||||||
|
|
@ -93,6 +102,10 @@
|
||||||
"label.veryHigh": "Çok Yüksek (FHD)",
|
"label.veryHigh": "Çok Yüksek (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Kapat",
|
"label.close": "Kapat",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Ayarlar",
|
"settings.settings": "Ayarlar",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -107,6 +120,8 @@
|
||||||
"settings.advancedMode": "Detaylı mod",
|
"settings.advancedMode": "Detaylı mod",
|
||||||
"settings.permanentTopBar": "Üst barı kalıcı yap",
|
"settings.permanentTopBar": "Üst barı kalıcı yap",
|
||||||
"settings.lastn": "İzlenebilir video sayısı",
|
"settings.lastn": "İzlenebilir video sayısı",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Dosya kaydedilemiyor",
|
"filesharing.saveFileError": "Dosya kaydedilemiyor",
|
||||||
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
|
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,14 @@
|
||||||
"room.clearFileSharing": null,
|
"room.clearFileSharing": null,
|
||||||
"room.speechUnsupported": null,
|
"room.speechUnsupported": null,
|
||||||
"room.moderatoractions": null,
|
"room.moderatoractions": null,
|
||||||
|
"room.raisedHand": null,
|
||||||
|
"room.loweredHand": null,
|
||||||
|
"room.extraVideo": null,
|
||||||
|
|
||||||
|
"me.mutedPTT": null,
|
||||||
|
|
||||||
|
"roles.gotRole": null,
|
||||||
|
"roles.lostRole": null,
|
||||||
|
|
||||||
"tooltip.login": "Увійти",
|
"tooltip.login": "Увійти",
|
||||||
"tooltip.logout": "Вихід",
|
"tooltip.logout": "Вихід",
|
||||||
|
|
@ -70,6 +78,7 @@
|
||||||
"tooltip.kickParticipant": null,
|
"tooltip.kickParticipant": null,
|
||||||
"tooltip.muteParticipant": null,
|
"tooltip.muteParticipant": null,
|
||||||
"tooltip.muteParticipantVideo": null,
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
|
||||||
"label.roomName": "Назва кімнати",
|
"label.roomName": "Назва кімнати",
|
||||||
"label.chooseRoomButton": "Продовжити",
|
"label.chooseRoomButton": "Продовжити",
|
||||||
|
|
@ -93,6 +102,10 @@
|
||||||
"label.veryHigh": "Дуже високий (FHD)",
|
"label.veryHigh": "Дуже високий (FHD)",
|
||||||
"label.ultra": "Ультра (UHD)",
|
"label.ultra": "Ультра (UHD)",
|
||||||
"label.close": "Закрити",
|
"label.close": "Закрити",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearence": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
|
||||||
"settings.settings": "Налаштування",
|
"settings.settings": "Налаштування",
|
||||||
"settings.camera": "Камера",
|
"settings.camera": "Камера",
|
||||||
|
|
@ -110,6 +123,8 @@
|
||||||
"settings.advancedMode": "Розширений режим",
|
"settings.advancedMode": "Розширений режим",
|
||||||
"settings.permanentTopBar": "Постійний верхній рядок",
|
"settings.permanentTopBar": "Постійний верхній рядок",
|
||||||
"settings.lastn": "Кількість видимих відео",
|
"settings.lastn": "Кількість видимих відео",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Неможливо зберегти файл",
|
"filesharing.saveFileError": "Неможливо зберегти файл",
|
||||||
"filesharing.startingFileShare": "Спроба поділитися файлом",
|
"filesharing.startingFileShare": "Спроба поділитися файлом",
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,8 @@ module.exports =
|
||||||
MODERATE_CHAT : [ userRoles.MODERATOR ],
|
MODERATE_CHAT : [ userRoles.MODERATOR ],
|
||||||
// The role(s) have permission to share screen
|
// The role(s) have permission to share screen
|
||||||
SHARE_SCREEN : [ userRoles.NORMAL ],
|
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
|
// The role(s) have permission to share files
|
||||||
SHARE_FILE : [ userRoles.NORMAL ],
|
SHARE_FILE : [ userRoles.NORMAL ],
|
||||||
// The role(s) have permission to moderate files
|
// The role(s) have permission to moderate files
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ class Lobby extends EventEmitter
|
||||||
return Object.values(this._peers).map((peer) =>
|
return Object.values(this._peers).map((peer) =>
|
||||||
({
|
({
|
||||||
peerId : peer.id,
|
peerId : peer.id,
|
||||||
displayName : peer.displayName
|
displayName : peer.displayName,
|
||||||
|
picture : peer.picture
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,8 @@ class Peer extends EventEmitter
|
||||||
id : this.id,
|
id : this.id,
|
||||||
displayName : this.displayName,
|
displayName : this.displayName,
|
||||||
picture : this.picture,
|
picture : this.picture,
|
||||||
roles : this.roles
|
roles : this.roles,
|
||||||
|
raisedHand : this.raisedHand
|
||||||
};
|
};
|
||||||
|
|
||||||
return peerInfo;
|
return peerInfo;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ const permissionsFromRoles =
|
||||||
SEND_CHAT : [ userRoles.NORMAL ],
|
SEND_CHAT : [ userRoles.NORMAL ],
|
||||||
MODERATE_CHAT : [ userRoles.MODERATOR ],
|
MODERATE_CHAT : [ userRoles.MODERATOR ],
|
||||||
SHARE_SCREEN : [ userRoles.NORMAL ],
|
SHARE_SCREEN : [ userRoles.NORMAL ],
|
||||||
|
EXTRA_VIDEO : [ userRoles.NORMAL ],
|
||||||
SHARE_FILE : [ userRoles.NORMAL ],
|
SHARE_FILE : [ userRoles.NORMAL ],
|
||||||
MODERATE_FILES : [ userRoles.MODERATOR ],
|
MODERATE_FILES : [ userRoles.MODERATOR ],
|
||||||
MODERATE_ROOM : [ userRoles.MODERATOR ],
|
MODERATE_ROOM : [ userRoles.MODERATOR ],
|
||||||
|
|
@ -530,6 +531,17 @@ class Room extends EventEmitter
|
||||||
peerId : peer.id,
|
peerId : peer.id,
|
||||||
role : newRole
|
role : newRole
|
||||||
}, true, true);
|
}, 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 }) =>
|
peer.on('lostRole', ({ oldRole }) =>
|
||||||
|
|
@ -586,13 +598,21 @@ class Room extends EventEmitter
|
||||||
.filter((joinedPeer) => joinedPeer.id !== peer.id)
|
.filter((joinedPeer) => joinedPeer.id !== peer.id)
|
||||||
.map((joinedPeer) => (joinedPeer.peerInfo));
|
.map((joinedPeer) => (joinedPeer.peerInfo));
|
||||||
|
|
||||||
|
const lobbyPeers = this._lobby.peerList();
|
||||||
|
|
||||||
cb(null, {
|
cb(null, {
|
||||||
roles : peer.roles,
|
roles : peer.roles,
|
||||||
peers : peerInfos,
|
peers : peerInfos,
|
||||||
tracker : config.fileTracker,
|
tracker : config.fileTracker,
|
||||||
authenticated : peer.authenticated,
|
authenticated : peer.authenticated,
|
||||||
permissionsFromRoles : permissionsFromRoles,
|
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.
|
// Mark the new Peer as joined.
|
||||||
|
|
@ -728,6 +748,13 @@ class Room extends EventEmitter
|
||||||
)
|
)
|
||||||
throw new Error('peer not authorized');
|
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.
|
// Ensure the Peer is joined.
|
||||||
if (!peer.joined)
|
if (!peer.joined)
|
||||||
throw new Error('Peer not yet joined');
|
throw new Error('Peer not yet joined');
|
||||||
|
|
@ -1067,26 +1094,6 @@ class Room extends EventEmitter
|
||||||
break;
|
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':
|
case 'lockRoom':
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
|
|
@ -1252,14 +1259,14 @@ class Room extends EventEmitter
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'raiseHand':
|
case 'raisedHand':
|
||||||
{
|
{
|
||||||
const { raisedHand } = request.data;
|
const { raisedHand } = request.data;
|
||||||
|
|
||||||
peer.raisedHand = raisedHand;
|
peer.raisedHand = raisedHand;
|
||||||
|
|
||||||
// Spread to others
|
// Spread to others
|
||||||
this._notification(peer.socket, 'raiseHand', {
|
this._notification(peer.socket, 'raisedHand', {
|
||||||
peerId : peer.id,
|
peerId : peer.id,
|
||||||
raisedHand : raisedHand
|
raisedHand : raisedHand
|
||||||
}, true);
|
}, true);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "multiparty-meeting-server",
|
"name": "multiparty-meeting-server",
|
||||||
"version": "3.2.0",
|
"version": "3.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "multiparty meeting server",
|
"description": "multiparty meeting server",
|
||||||
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue