Compare commits

..

10 Commits

Author SHA1 Message Date
Pietro Brenna d878a809d1 Merge branch 'build_system2' into auto_join
Briq/multiparty-meeting/pipeline/head This commit looks good Details
2020-05-14 18:47:48 +02:00
Pietro Brenna 1389107107 Aggiunto build system docker
Briq/multiparty-meeting/pipeline/head This commit looks good Details
2020-05-14 18:30:46 +02:00
Pietro Brenna 59ba0499cd Tolgo cookie consent 2020-05-13 14:43:59 +02:00
Pietro Brenna 8f87b1a3c1 Tasto leave chiude finestra 2020-05-13 14:43:59 +02:00
Pietro Brenna e03ab517e0 Fix temporaneo (bisognerebbe lavorare in App.js!) 2020-05-13 14:43:59 +02:00
Mert ÇELEN bb02cee8ff Create main.yml 2020-05-13 14:43:59 +02:00
Mészáros Mihály 700f48b15c Fix: bump version number 2020-05-13 14:43:59 +02:00
Mészáros Mihály 9f8bc7896c clarify more that ip change is mandatory
Move to documentation IP range
2020-05-13 14:43:59 +02:00
Mészáros Mihály aeea4c76bf Add turkish translation 2020-05-13 14:43:59 +02:00
Ali Orhun Akkirman f8d0a45dcc Create turkish language file 2020-05-13 14:43:59 +02:00
116 changed files with 3512 additions and 12252 deletions

View File

@ -1,40 +1,5 @@
# Changelog
## 3.3
* Add: Rooms now scale across cores
* Add: Permissions and roles. Users can now have different roles (moderator, admin etc.) that give different permissions.
* Add: TURN API or fallback TURN server
* Add: Configurable room size limit
* Add: Prometheus monitoring support
* Add: Possible to share several videos (ex: 2 webcams)
* Add: Configurable audio settings (echocancellation etc.)
* Add: Configurable audio output device (in supported browsers)
* Add: Audio auto mute/unmute based on volume
* Add: Handle unsupported browsers properly
* Add: Lots of appearence settings
* Add: Side drawer can now stay permanently open
* Add: Move control buttons to separate control bar
* Add: Can now "raise hand"
* Add: Screen sharing in Safari 13+, Opera and Edge
* Add: Extended advanced info about network in client
* Add: Configurable screen sharing frame rate
* Add: Help and About dialogs
* Add: More keyboard shortcuts
* Add: Quality indicator on videos
* Add: More translations
* Fix: Various UI fixes and improvements
* Fix: Better audio/video device handling
* Fix: Update keyboard shortcut handling
* Fix: Authentication for load balanced scenarios
* Fix: Signaling when entering lobby
* Fix: Signaling timeouts and retries
* Fix: Filesharing fixes (sharing same file twice, etc.)
* Fix: Better handling of hark
* Fix: Use applyContraints instead of restarting producers
* Fix: Now handles reconnects properly if client loses connection
* Fix: Rotating devices don't show rotated videos
* Fix: Various fixes to client authentication
## 3.2.1
* Fix: permananent top bar by default

View File

@ -1 +0,0 @@
Source code contributions should pass static code analysis as performed by `npm run lint` in `server` and `app` respectively.

View File

@ -62,6 +62,14 @@ OR
## Configure multiparty-meeting servers
### App config
mm/configs/app/config.js
``` js
multipartyServer : 'meet.example.com',
```
### Server config
mm/configs/server/config.js

View File

@ -1,21 +0,0 @@
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.

View File

@ -23,11 +23,11 @@ If you want the ansible approach, you can find ansible role [here](https://githu
## Manual installation
* Prerequisites:
Currently multiparty-meeting will only run on nodejs v13.x
Currently multiparty-meeting will only run on nodejs v10.*
To install see here [here](https://github.com/nodesource/distributions/blob/master/README.md#debinstall).
```bash
$ sudo apt install git npm build-essential redis
$ sudo apt install npm build-essential redis
```
* Clone the project:
@ -113,7 +113,7 @@ To integrate with an LMS (e.g. Moodle), have a look at [LTI](LTI/LTI.md).
## TURN configuration
* You need an additional [TURN](https://github.com/coturn/coturn)-server for clients located behind restrictive firewalls! Add your server and credentials to `server/config/config.js`
* You need an additional [TURN](https://github.com/coturn/coturn)-server for clients located behind restrictive firewalls! Add your server and credentials to `app/public/config/config.js`
## Community-driven support
@ -134,7 +134,7 @@ This started as a fork of the [work](https://github.com/versatica/mediasoup-demo
## License
MIT License (see `LICENSE.md`)
MIT
Contributions to this work were made on behalf of the GÉANT project, a project that has received funding from the European Unions 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.

View File

@ -1,2 +0,0 @@
REACT_APP_VERSION=$npm_package_version
REACT_APP_NAME=$npm_package_name

View File

@ -159,12 +159,6 @@
"no-inner-declarations": 2,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-trailing-spaces": [
"error",
{
"ignoreComments": true
}
],
"no-lonely-if": 2,
"no-mixed-operators": 2,
"no-mixed-spaces-and-tabs": 2,

View File

@ -1,6 +1,6 @@
{
"name": "multiparty-meeting",
"version": "3.3.0",
"version": "3.2.1",
"private": true,
"description": "multiparty meeting service",
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
@ -20,19 +20,17 @@
"hark": "^1.2.3",
"is-electron": "^2.2.0",
"marked": "^0.8.0",
"mediasoup-client": "^3.6.5",
"mediasoup-client": "^3.5.4",
"notistack": "^0.9.5",
"prop-types": "^15.7.2",
"random-string": "^0.2.0",
"react": "^16.10.2",
"react-cookie-consent": "^2.5.0",
"react-dom": "^16.10.2",
"react-flip-toolkit": "^7.0.9",
"react-intl": "^3.4.0",
"react-redux": "^7.1.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1",
"react-wakelock-react16": "0.0.7",
"react-scripts": "3.0.1",
"redux": "^4.0.4",
"redux-logger": "^3.0.6",
"redux-persist": "^6.0.0",
@ -50,8 +48,7 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"electron": "electron --no-sandbox .",
"dev": "nf start -p 3000",
"lint": "eslint -c .eslintrc.json --ext .js src"
"dev": "nf start -p 3000"
},
"browserslist": [
">0.2%",
@ -61,7 +58,6 @@
],
"devDependencies": {
"electron": "^7.1.1",
"eslint-plugin-react": "^7.19.0",
"foreman": "^3.0.1",
"redux-mock-store": "^1.5.3"
}

View File

@ -4,50 +4,25 @@ var config =
loginEnabled : false,
developmentPort : 3443,
productionPort : 443,
/**
* Supported browsers version
* in bowser satisfy format.
* See more:
* https://www.npmjs.com/package/bowser#filtering-browsers
* Otherwise you got a unsupported browser page
*/
supportedBrowsers :
multipartyServer : 'letsmeet.no',
turnServers : [
{
'windows' : {
'internet explorer' : '>12',
'microsoft edge' : '>18'
},
'safari' : '>12',
'firefox' : '>=60',
'chrome' : '>=74',
'chromium' : '>=74',
'opera' : '>=62',
'samsung internet for android' : '>=11.1.1.52'
},
urls : [
'turn:turn.example.com:443?transport=tcp'
],
username : 'example',
credential : 'example'
}
],
/**
* Resolutions:
*
* If defaultResolution is set, it will override user settings when joining:
* low ~ 320x240
* medium ~ 640x480
* high ~ 1280x720
* veryhigh ~ 1920x1080
* ultra ~ 3840x2560
*
**/
/**
* Frame rates:
*
* 1, 5, 10, 15, 20, 25, 30
*
**/
defaultResolution : 'medium',
defaultFrameRate : 15,
defaultScreenResolution : 'veryhigh',
defaultScreenSharingFrameRate : 5,
// Enable or disable simulcast for webcam video
simulcast : true,
// Enable or disable simulcast for screen sharing video
@ -59,89 +34,18 @@ var config =
{ scaleResolutionDownBy: 2 },
{ scaleResolutionDownBy: 1 }
],
/**
* Alternative simulcast setting:
* [
* { maxBitRate: 50000 },
* { maxBitRate: 1000000 },
* { maxBitRate: 4800000 }
*],
**/
/**
* White listing browsers that support audio output device selection.
* It is not yet fully implemented in Firefox.
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=1498512
*/
audioOutputSupportedBrowsers :
[
'chrome',
'opera'
],
// Socket.io request timeout
requestTimeout : 20000,
requestRetries : 3,
requestTimeout : 10000,
transportOptions :
{
tcp : true
},
defaultAudio :
{
sampleRate : 48000,
channelCount : 1,
volume : 1.0,
autoGainControl : true,
echoCancellation : true,
noiseSuppression : true,
voiceActivityMute : false,
sampleSize : 16
},
/**
* Set max number participants in one room that join
* unmuted. Next participant will join automatically muted
* Default value is 4
*
* Set it to 0 to auto mute all,
* Set it to negative (-1) to never automatically auto mute
* but use it with caution
* full mesh audio strongly decrease room capacity!
*/
autoMuteThreshold : 4,
background : 'images/background.jpg',
defaultLayout : 'democratic', // democratic, filmstrip
// If true, will show media control buttons in separate
// control bar, not in the ME container.
buttonControlBar : false,
// If false, will push videos away to make room for side
// drawer. If true, will overlay side drawer over videos
drawerOverlayed : true,
// Position of notifications
notificationPosition : 'right',
// Timeout for autohiding topbar and button control bar
hideTimeout : 3000,
// max number of participant that will be visible in
// as speaker
lastN : 4,
mobileLastN : 1,
// Highest number of lastN the user can select manually in
// userinteface
maxLastN : 5,
// If truthy, users can NOT change number of speakers visible
lockLastN : false,
background : 'images/background.jpg',
// Add file and uncomment for adding logo to appbar
// logo : 'images/logo.svg',
title : 'Multiparty meeting',
// Service & Support URL
// if not set then not displayed on the about modals
supportUrl : 'https://support.example.com',
// Privacy and dataprotection URL or path
// by default privacy/privacy.html
// that is a placeholder for your policies
//
// but an external url could be also used here
privacyUrl : 'static/privacy.html',
theme :
{
palette :

View File

@ -16,44 +16,6 @@
<title>Multiparty Meeting</title>
<script src='%PUBLIC_URL%/config/config.js' type='text/javascript'></script>
<!-- Show an error page to IE browsers -->
<script type='text/javascript'>
var fallback = '<style type="text/css">body{margin:40px auto;max-width:650px;line-height:1.6;font-size:18px;color:#444;padding:0 10px}h1,h2,h3{line-height:1.2}</style><header><h1>Your browser is not supported</h1><aside>You need to change to a different browser.</aside></header><h3>Supported browsers</h3><ul><li>Google Chrome/Chromium 55 +</li><li>Microsoft Edge 18 +</li><li>Mozilla Firefox 60 +</li><li>Apple Safari 12 +</li><li>Opera 62 +</li><li>Samsung Internet 11.1.1.52 +</li></ul>';
var fallbackCall = function() {
document.body.innerHTML = fallback;
};
if(navigator.userAgent.indexOf('MSIE') !== -1)
{
document.attachEvent('onreadystatechange', function() {
if (document.readyState === 'complete')
{
document.detachEvent('onreadystatechange', arguments.callee);
fallbackCall();
}
});
}
if (navigator.appVersion.indexOf('Trident/') > -1)
{
if (
document.readyState === 'complete' ||
(document.readyState !== 'loading' && !document.documentElement.doScroll)
)
{
document.removeEventListener('DOMContentLoaded', fallbackCall);
fallbackCall();
}
else
{
document.addEventListener('DOMContentLoaded', fallbackCall);
}
}
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Placeholder for Privacy Statement / Policy, AUP</title>
<style type='text/css'>body{margin:40px auto;max-width:650px;line-height:1.6;font-size:18px;color:#444;padding:0 10px}h1,h2,h3{line-height:1.2}</style>
</head>
<body>
<header><h1>Privacy Statement</h1></header>
<h2>Privacy Policy</h2>
<ul>
<li>User consent</li>
<li>Data deletion</li>
</ul>
<h2>Acceptable use policy (AUP)</h2>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -213,8 +213,6 @@ export default class ScreenShare
{
if (isElectron())
return new ElectronScreenShare();
else if (device.platform !== 'desktop')
return new DefaultScreenShare();
else
{
switch (device.flag)
@ -226,17 +224,11 @@ export default class ScreenShare
else
return new DisplayMediaScreenShare();
}
case 'safari':
{
if (device.version >= 13.0)
return new DisplayMediaScreenShare();
else
return new DefaultScreenShare();
}
case 'chrome':
case 'chromium':
case 'opera':
case 'edge':
{
return new DisplayMediaScreenShare();
}
case 'msedge':
{
return new DisplayMediaScreenShare();
}

View File

@ -12,7 +12,6 @@ export default class Spotlights extends EventEmitter
this._signalingSocket = signalingSocket;
this._maxSpotlights = maxSpotlights;
this._peerList = [];
this._unmutablePeerList = [];
this._selectedSpotlights = [];
this._currentSpotlights = [];
this._started = false;
@ -46,74 +45,6 @@ export default class Spotlights extends EventEmitter
}
}
getNextAsSelected(peerId)
{
let newSelectedPeer = null;
if (peerId == null && this._unmutablePeerList.length > 0)
{
peerId = this._unmutablePeerList[0];
}
if (peerId != null && this._currentSpotlights.length < this._unmutablePeerList.length)
{
const oldIndex = this._unmutablePeerList.indexOf(peerId);
let index = oldIndex;
index++;
for (let i = 0; i < this._unmutablePeerList.length; i++)
{
if (index >= this._unmutablePeerList.length)
{
index = 0;
}
newSelectedPeer = this._unmutablePeerList[index];
if (!this._currentSpotlights.includes(newSelectedPeer))
{
break;
}
index++;
}
}
return newSelectedPeer;
}
getPrevAsSelected(peerId)
{
let newSelectedPeer = null;
if (peerId == null && this._unmutablePeerList.length > 0)
{
peerId = this._unmutablePeerList[0];
}
if (peerId != null && this._currentSpotlights.length < this._unmutablePeerList.length)
{
const oldIndex = this._unmutablePeerList.indexOf(peerId);
let index = oldIndex;
index--;
for (let i = 0; i < this._unmutablePeerList.length; i++)
{
if (index < 0)
{
index = this._unmutablePeerList.length - 1;
}
newSelectedPeer = this._unmutablePeerList[index];
if (!this._currentSpotlights.includes(newSelectedPeer))
{
break;
}
index--;
}
}
return newSelectedPeer;
}
setPeerSpotlight(peerId)
{
logger.debug('setPeerSpotlight() [peerId:"%s"]', peerId);
@ -164,15 +95,6 @@ export default class Spotlights extends EventEmitter
});
}
clearSpotlights()
{
this._started = false;
this._peerList = [];
this._selectedSpotlights = [];
this._currentSpotlights = [];
}
_newPeer(id)
{
logger.debug(
@ -183,7 +105,6 @@ export default class Spotlights extends EventEmitter
logger.debug('_handlePeer() | adding peer [peerId: "%s"]', id);
this._peerList.push(id);
this._unmutablePeerList.push(id);
if (this._started)
this._spotlightsUpdated();
@ -195,10 +116,19 @@ export default class Spotlights extends EventEmitter
logger.debug(
'room "peerClosed" event [peerId:%o]', id);
this._peerList = this._peerList.filter((peer) => peer !== id);
this._unmutablePeerList = this._unmutablePeerList.filter((peer) => peer !== id);
let index = this._peerList.indexOf(id);
this._selectedSpotlights = this._selectedSpotlights.filter((peer) => peer !== id);
if (index !== -1) // We have this peer in the list, remove
{
this._peerList.splice(index, 1);
}
index = this._selectedSpotlights.indexOf(id);
if (index !== -1) // We have this peer in the list, remove
{
this._selectedSpotlights.splice(index, 1);
}
if (this._started)
this._spotlightsUpdated();

View File

@ -31,8 +31,6 @@ beforeEach(() =>
me : {
audioDevices : null,
audioInProgress : false,
audioOutputDevices : null,
audioOutputInProgress : false,
canSendMic : false,
canSendWebcam : false,
canShareFiles : false,
@ -42,8 +40,8 @@ beforeEach(() =>
loggedIn : false,
loginEnabled : true,
picture : null,
raisedHand : false,
raisedHandInProgress : false,
raiseHand : false,
raiseHandInProgress : false,
screenShareInProgress : false,
webcamDevices : null,
webcamInProgress : false
@ -78,7 +76,6 @@ beforeEach(() =>
displayName : 'Jest Tester',
resolution : 'ultra',
selectedAudioDevice : 'default',
selectedAudioOutputDevice : 'default',
selectedWebcam : 'soifjsiajosjfoi'
},
toolarea : {

View File

@ -15,8 +15,3 @@ export const addChatHistory = (chatHistory) =>
type : 'ADD_CHAT_HISTORY',
payload : { chatHistory }
});
export const clearChat = () =>
({
type : 'CLEAR_CHAT'
});

View File

@ -10,11 +10,6 @@ export const removeConsumer = (consumerId, peerId) =>
payload : { consumerId, peerId }
});
export const clearConsumers = () =>
({
type : 'CLEAR_CONSUMERS'
});
export const setConsumerPaused = (consumerId, originator) =>
({
type : 'SET_CONSUMER_PAUSED',
@ -40,10 +35,12 @@ export const setConsumerPreferredLayers = (consumerId, spatialLayer, temporalLay
});
export const setConsumerPriority = (consumerId, priority) =>
({
{
return {
type : 'SET_CONSUMER_PRIORITY',
payload : { consumerId, priority }
});
};
};
export const setConsumerTrack = (consumerId, track) =>
({

View File

@ -33,8 +33,3 @@ export const setFileDone = (magnetUri, sharedFiles) =>
type : 'SET_FILE_DONE',
payload : { magnetUri, sharedFiles }
});
export const clearFiles = () =>
({
type : 'CLEAR_FILES'
});

View File

@ -4,30 +4,12 @@ export const setMe = ({ peerId, loginEnabled }) =>
payload : { peerId, loginEnabled }
});
export const setBrowser = (browser) =>
({
type : 'SET_BROWSER',
payload : { browser }
});
export const loggedIn = (flag) =>
({
type : 'LOGGED_IN',
payload : { flag }
});
export const addRole = (role) =>
({
type : 'ADD_ROLE',
payload : { role }
});
export const removeRole = (role) =>
({
type : 'REMOVE_ROLE',
payload : { role }
});
export const setPicture = (picture) =>
({
type : 'SET_PICTURE',
@ -51,21 +33,15 @@ export const setAudioDevices = (devices) =>
payload : { devices }
});
export const setAudioOutputDevices = (devices) =>
({
type : 'SET_AUDIO_OUTPUT_DEVICES',
payload : { devices }
});
export const setWebcamDevices = (devices) =>
({
type : 'SET_WEBCAM_DEVICES',
payload : { devices }
});
export const setRaisedHand = (flag) =>
export const setMyRaiseHandState = (flag) =>
({
type : 'SET_RAISED_HAND',
type : 'SET_MY_RAISE_HAND_STATE',
payload : { flag }
});
@ -75,12 +51,6 @@ export const setAudioInProgress = (flag) =>
payload : { flag }
});
export const setAudioOutputInProgress = (flag) =>
({
type : 'SET_AUDIO_OUTPUT_IN_PROGRESS',
payload : { flag }
});
export const setWebcamInProgress = (flag) =>
({
type : 'SET_WEBCAM_IN_PROGRESS',
@ -93,9 +63,9 @@ export const setScreenShareInProgress = (flag) =>
payload : { flag }
});
export const setRaisedHandInProgress = (flag) =>
export const setMyRaiseHandStateInProgress = (flag) =>
({
type : 'SET_RAISED_HAND_IN_PROGRESS',
type : 'SET_MY_RAISE_HAND_STATE_IN_PROGRESS',
payload : { flag }
});
@ -104,15 +74,3 @@ export const setDisplayNameInProgress = (flag) =>
type : 'SET_DISPLAY_NAME_IN_PROGRESS',
payload : { flag }
});
export const setIsSpeaking = (flag) =>
({
type : 'SET_IS_SPEAKING',
payload : { flag }
});
export const setAutoMuted = (flag) =>
({
type : 'SET_AUTO_MUTED',
payload : { flag }
});

View File

@ -10,11 +10,6 @@ export const removePeer = (peerId) =>
payload : { peerId }
});
export const clearPeers = () =>
({
type : 'CLEAR_PEERS'
});
export const setPeerDisplayName = (displayName, peerId) =>
({
type : 'SET_PEER_DISPLAY_NAME',
@ -39,16 +34,10 @@ export const setPeerScreenInProgress = (peerId, flag) =>
payload : { peerId, flag }
});
export const setPeerRaisedHand = (peerId, raisedHand, raisedHandTimestamp) =>
export const setPeerRaiseHandState = (peerId, raiseHandState) =>
({
type : 'SET_PEER_RAISED_HAND',
payload : { peerId, raisedHand, raisedHandTimestamp }
});
export const setPeerRaisedHandInProgress = (peerId, flag) =>
({
type : 'SET_PEER_RAISED_HAND_IN_PROGRESS',
payload : { peerId, flag }
type : 'SET_PEER_RAISE_HAND_STATE',
payload : { peerId, raiseHandState }
});
export const setPeerPicture = (peerId, picture) =>
@ -56,39 +45,3 @@ export const setPeerPicture = (peerId, picture) =>
type : 'SET_PEER_PICTURE',
payload : { peerId, picture }
});
export const addPeerRole = (peerId, role) =>
({
type : 'ADD_PEER_ROLE',
payload : { peerId, role }
});
export const removePeerRole = (peerId, role) =>
({
type : 'REMOVE_PEER_ROLE',
payload : { peerId, role }
});
export const setPeerKickInProgress = (peerId, flag) =>
({
type : 'SET_PEER_KICK_IN_PROGRESS',
payload : { peerId, flag }
});
export const setMutePeerInProgress = (peerId, flag) =>
({
type : 'STOP_PEER_AUDIO_IN_PROGRESS',
payload : { peerId, flag }
});
export const setStopPeerVideoInProgress = (peerId, flag) =>
({
type : 'STOP_PEER_VIDEO_IN_PROGRESS',
payload : { peerId, flag }
});
export const setStopPeerScreenSharingInProgress = (peerId, flag) =>
({
type : 'STOP_PEER_SCREEN_SHARING_IN_PROGRESS',
payload : { peerId, flag }
});

View File

@ -40,12 +40,6 @@ export const setSignInRequired = (signInRequired) =>
payload : { signInRequired }
});
export const setOverRoomLimit = (overRoomLimit) =>
({
type : 'SET_OVER_ROOM_LIMIT',
payload : { overRoomLimit }
});
export const setAccessCode = (accessCode) =>
({
type : 'SET_ACCESS_CODE',
@ -58,37 +52,13 @@ export const setJoinByAccessCode = (joinByAccessCode) =>
payload : { joinByAccessCode }
});
export const setSettingsOpen = (settingsOpen) =>
export const setSettingsOpen = ({ settingsOpen }) =>
({
type : 'SET_SETTINGS_OPEN',
payload : { settingsOpen }
});
export const setExtraVideoOpen = (extraVideoOpen) =>
({
type : 'SET_EXTRA_VIDEO_OPEN',
payload : { extraVideoOpen }
});
export const setHelpOpen = (helpOpen) =>
({
type : 'SET_HELP_OPEN',
payload : { helpOpen }
});
export const setAboutOpen = (aboutOpen) =>
({
type : 'SET_ABOUT_OPEN',
payload : { aboutOpen }
});
export const setSettingsTab = (tab) =>
({
type : 'SET_SETTINGS_TAB',
payload : { tab }
});
export const setLockDialogOpen = (lockDialogOpen) =>
export const setLockDialogOpen = ({ lockDialogOpen }) =>
({
type : 'SET_LOCK_DIALOG_OPEN',
payload : { lockDialogOpen }
@ -130,11 +100,6 @@ export const setSpotlights = (spotlights) =>
payload : { spotlights }
});
export const clearSpotlights = () =>
({
type : 'CLEAR_SPOTLIGHTS'
});
export const toggleJoined = () =>
({
type : 'TOGGLE_JOINED'
@ -145,57 +110,3 @@ export const toggleConsumerFullscreen = (consumerId) =>
type : 'TOGGLE_FULLSCREEN_CONSUMER',
payload : { consumerId }
});
export const setLobbyPeersPromotionInProgress = (flag) =>
({
type : 'SET_LOBBY_PEERS_PROMOTION_IN_PROGRESS',
payload : { flag }
});
export const setMuteAllInProgress = (flag) =>
({
type : 'MUTE_ALL_IN_PROGRESS',
payload : { flag }
});
export const setStopAllVideoInProgress = (flag) =>
({
type : 'STOP_ALL_VIDEO_IN_PROGRESS',
payload : { flag }
});
export const setStopAllScreenSharingInProgress = (flag) =>
({
type : 'STOP_ALL_SCREEN_SHARING_IN_PROGRESS',
payload : { flag }
});
export const setCloseMeetingInProgress = (flag) =>
({
type : 'CLOSE_MEETING_IN_PROGRESS',
payload : { flag }
});
export const setClearChatInProgress = (flag) =>
({
type : 'CLEAR_CHAT_IN_PROGRESS',
payload : { flag }
});
export const setClearFileSharingInProgress = (flag) =>
({
type : 'CLEAR_FILE_SHARING_IN_PROGRESS',
payload : { flag }
});
export const setRoomPermissions = (roomPermissions) =>
({
type : 'SET_ROOM_PERMISSIONS',
payload : { roomPermissions }
});
export const setAllowWhenRoleMissing = (allowWhenRoleMissing) =>
({
type : 'SET_ALLOW_WHEN_ROLE_MISSING',
payload : { allowWhenRoleMissing }
});

View File

@ -4,12 +4,6 @@ export const setSelectedAudioDevice = (deviceId) =>
payload : { deviceId }
});
export const setSelectedAudioOutputDevice = (deviceId) =>
({
type : 'CHANGE_AUDIO_OUTPUT_DEVICE',
payload : { deviceId }
});
export const setSelectedWebcamDevice = (deviceId) =>
({
type : 'CHANGE_WEBCAM',
@ -22,24 +16,6 @@ export const setVideoResolution = (resolution) =>
payload : { resolution }
});
export const setVideoFrameRate = (frameRate) =>
({
type : 'SET_VIDEO_FRAME_RATE',
payload : { frameRate }
});
export const setScreenSharingResolution = (screenSharingResolution) =>
({
type : 'SET_SCREEN_SHARING_RESOLUTION',
payload : { screenSharingResolution }
});
export const setScreenSharingFrameRate = (screenSharingFrameRate) =>
({
type : 'SET_SCREEN_SHARING_FRAME_RATE',
payload : { screenSharingFrameRate }
});
export const setDisplayName = (displayName) =>
({
type : 'SET_DISPLAY_NAME',
@ -56,67 +32,6 @@ export const togglePermanentTopBar = () =>
type : 'TOGGLE_PERMANENT_TOPBAR'
});
export const toggleButtonControlBar = () =>
({
type : 'TOGGLE_BUTTON_CONTROL_BAR'
});
export const toggleDrawerOverlayed = () =>
({
type : 'TOGGLE_DRAWER_OVERLAYED'
});
export const toggleShowNotifications = () =>
({
type : 'TOGGLE_SHOW_NOTIFICATIONS'
});
export const setEchoCancellation = (echoCancellation) =>
({
type : 'SET_ECHO_CANCELLATION',
payload : { echoCancellation }
});
export const setAutoGainControl = (autoGainControl) =>
({
type : 'SET_AUTO_GAIN_CONTROL',
payload : { autoGainControl }
});
export const setNoiseSuppression = (noiseSuppression) =>
({
type : 'SET_NOISE_SUPPRESSION',
payload : { noiseSuppression }
});
export const setVoiceActivatedUnmute = (voiceActivatedUnmute) =>
({
type : 'SET_VOICE_ACTIVATED_UNMUTE',
payload : { voiceActivatedUnmute }
});
export const setNoiseThreshold = (noiseThreshold) =>
({
type : 'SET_NOISE_THRESHOLD',
payload : { noiseThreshold }
});
export const setDefaultAudio = (audio) =>
({
type : 'SET_DEFAULT_AUDIO',
payload : { audio }
});
export const toggleHiddenControls = () =>
({
type : 'TOGGLE_HIDDEN_CONTROLS'
});
export const toggleNotificationSounds = () =>
({
type : 'TOGGLE_NOTIFICATION_SOUNDS'
});
export const setLastN = (lastN) =>
({
type : 'SET_LAST_N',

View File

@ -1,5 +0,0 @@
export const addTransportStats = (transport, type) =>
({
type : 'ADD_TRANSPORT_STATS',
payload : { transport, type }
});

View File

@ -5,20 +5,74 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import { permissions } from '../../../permissions';
import { makePermissionSelector } from '../../Selectors';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import IconButton from '@material-ui/core/IconButton';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
import PromoteIcon from '@material-ui/icons/OpenInBrowser';
import Tooltip from '@material-ui/core/Tooltip';
const styles = () =>
const styles = (theme) =>
({
root :
{
padding : theme.spacing(1),
width : '100%',
overflow : 'hidden',
cursor : 'auto',
display : 'flex'
},
avatar :
{
borderRadius : '50%',
height : '2rem'
},
peerInfo :
{
fontSize : '1rem',
border : 'none',
display : 'flex',
paddingLeft : theme.spacing(1),
flexGrow : 1,
alignItems : 'center'
},
controls :
{
float : 'right',
display : 'flex',
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'center'
},
button :
{
flex : '0 0 auto',
margin : '0.3rem',
borderRadius : 2,
backgroundColor : 'rgba(0, 0, 0, 0.5)',
cursor : 'pointer',
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
},
'&.disabled' :
{
pointerEvents : 'none',
backgroundColor : 'var(--media-control-botton-disabled)'
},
'&.promote' :
{
backgroundColor : 'var(--media-control-botton-on)'
}
},
ListItem :
{
alignItems : 'center'
}
@ -29,8 +83,6 @@ const ListLobbyPeer = (props) =>
const {
roomClient,
peer,
promotionInProgress,
canPromote,
classes
} = props;
@ -40,7 +92,7 @@ const ListLobbyPeer = (props) =>
return (
<ListItem
className={classnames(classes.root)}
className={classnames(classes.ListItem)}
key={peer.peerId}
button
alignItems='flex-start'
@ -57,13 +109,10 @@ const ListLobbyPeer = (props) =>
defaultMessage : 'Click to let them in'
})}
>
<IconButton
disabled={
!canPromote ||
peer.promotionInProgress ||
promotionInProgress
}
color='primary'
<ListItemIcon
className={classnames(classes.button, 'promote', {
disabled : peer.promotionInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
@ -71,7 +120,7 @@ const ListLobbyPeer = (props) =>
}}
>
<PromoteIcon />
</IconButton>
</ListItemIcon>
</Tooltip>
</ListItem>
);
@ -82,38 +131,24 @@ ListLobbyPeer.propTypes =
roomClient : PropTypes.any.isRequired,
advancedMode : PropTypes.bool,
peer : PropTypes.object.isRequired,
promotionInProgress : PropTypes.bool.isRequired,
canPromote : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = (initialState, { id }) =>
{
const hasPermission = makePermissionSelector(permissions.PROMOTE_PEER);
const mapStateToProps = (state) =>
const mapStateToProps = (state, { id }) =>
{
return {
peer : state.lobbyPeers[id],
promotionInProgress : state.room.lobbyPeersPromotionInProgress,
canPromote : hasPermission(state)
peer : state.lobbyPeers[id]
};
};
return mapStateToProps;
};
export default withRoomContext(connect(
makeMapStateToProps,
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.peers === next.peers && // For checking permissions
prev.me.roles === next.me.roles &&
prev.lobbyPeers === next.lobbyPeers
);
}

View File

@ -1,10 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
import {
lobbyPeersKeySelector,
makePermissionSelector
lobbyPeersKeySelector
} from '../../Selectors';
import { permissions } from '../../../permissions';
import * as appPropTypes from '../../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
@ -17,6 +15,14 @@ import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
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 ListSubheader from '@material-ui/core/ListSubheader';
import ListLobbyPeer from './ListLobbyPeer';
@ -53,11 +59,11 @@ const styles = (theme) =>
});
const LockDialog = ({
roomClient,
// roomClient,
room,
handleCloseLockDialog,
// handleAccessCode,
lobbyPeers,
canPromote,
classes
}) =>
{
@ -65,7 +71,7 @@ const LockDialog = ({
<Dialog
className={classes.root}
open={room.lockDialogOpen}
onClose={() => handleCloseLockDialog(false)}
onClose={() => handleCloseLockDialog({ lockDialogOpen: false })}
classes={{
paper : classes.dialogPaper
}}
@ -76,6 +82,54 @@ const LockDialog = ({
defaultMessage='Lobby administration'
/>
</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 ?
<List
dense
@ -106,21 +160,7 @@ const LockDialog = ({
</DialogContent>
}
<DialogActions>
<Button
disabled={
lobbyPeers.length === 0 ||
!canPromote ||
room.lobbyPeersPromotionInProgress
}
onClick={() => roomClient.promoteAllLobbyPeers()}
color='primary'
>
<FormattedMessage
id='label.promoteAllPeers'
defaultMessage='Promote all'
/>
</Button>
<Button onClick={() => handleCloseLockDialog(false)} color='primary'>
<Button onClick={() => handleCloseLockDialog({ lockDialogOpen: false })} color='primary'>
<FormattedMessage
id='label.close'
defaultMessage='Close'
@ -133,47 +173,41 @@ const LockDialog = ({
LockDialog.propTypes =
{
roomClient : PropTypes.object.isRequired,
// roomClient : PropTypes.any.isRequired,
room : appPropTypes.Room.isRequired,
handleCloseLockDialog : PropTypes.func.isRequired,
handleAccessCode : PropTypes.func.isRequired,
lobbyPeers : PropTypes.array,
canPromote : PropTypes.bool,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.PROMOTE_PEER);
const mapStateToProps = (state) =>
{
return {
room : state.room,
lobbyPeers : lobbyPeersKeySelector(state),
canPromote : hasPermission(state)
lobbyPeers : lobbyPeersKeySelector(state)
};
};
return mapStateToProps;
};
const mapDispatchToProps = {
handleCloseLockDialog : roomActions.setLockDialogOpen,
handleAccessCode : roomActions.setAccessCode
};
export default withRoomContext(connect(
makeMapStateToProps,
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me.roles === next.me.roles &&
prev.peers === next.peers &&
prev.room.locked === next.room.locked &&
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
);
}

View File

@ -1,15 +1,21 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../RoomContext';
import isElectron from 'is-electron';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import randomString from 'random-string';
import Dialog from '@material-ui/core/Dialog';
import DialogContentText from '@material-ui/core/DialogContentText';
import IconButton from '@material-ui/core/IconButton';
import AccountCircle from '@material-ui/icons/AccountCircle';
import Avatar from '@material-ui/core/Avatar';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Tooltip from '@material-ui/core/Tooltip';
import CookieConsent from 'react-cookie-consent';
import MuiDialogTitle from '@material-ui/core/DialogTitle';
import MuiDialogContent from '@material-ui/core/DialogContent';
@ -82,12 +88,63 @@ const styles = (theme) =>
const DialogTitle = withStyles(styles)((props) =>
{
const { children, classes, ...other } = props;
const [ open, setOpen ] = useState(false);
const intl = useIntl();
useEffect(() =>
{
const openTimer = setTimeout(() => setOpen(true), 1000);
const closeTimer = setTimeout(() => setOpen(false), 4000);
return () =>
{
clearTimeout(openTimer);
clearTimeout(closeTimer);
};
}, []);
const { children, classes, myPicture, onLogin, ...other } = props;
const handleTooltipClose = () =>
{
setOpen(false);
};
const handleTooltipOpen = () =>
{
setOpen(true);
};
return (
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
{ window.config && window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
<Typography variant='h5'>{children}</Typography>
{ window.config && window.config.loginEnabled &&
<Tooltip
onClose={handleTooltipClose}
onOpen={handleTooltipOpen}
open={open}
title={intl.formatMessage({
id : 'tooltip.login',
defaultMessage : 'Click to log in'
})}
placement='left'
>
<IconButton
aria-label='Account'
className={classes.loginButton}
color='inherit'
onClick={onLogin}
>
{ myPicture ?
<Avatar src={myPicture} className={classes.largeAvatar} />
:
<AccountCircle className={classes.largeIcon} />
}
</IconButton>
</Tooltip>
}
</MuiDialogTitle>
);
});
@ -108,6 +165,9 @@ const DialogActions = withStyles((theme) => ({
}))(MuiDialogActions);
const ChooseRoom = ({
roomClient,
loggedIn,
myPicture,
classes
}) =>
{
@ -124,8 +184,14 @@ const ChooseRoom = ({
paper : classes.dialogPaper
}}
>
<DialogTitle>
{ window.config.title ? window.config.title : 'Multiparty meeting' }
<DialogTitle
myPicture={myPicture}
onLogin={() =>
{
loggedIn ? roomClient.logout() : roomClient.login();
}}
>
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
<hr />
</DialogTitle>
<DialogContent>
@ -178,8 +244,7 @@ const ChooseRoom = ({
<CookieConsent buttonText={intl.formatMessage({
id : 'room.consentUnderstand',
defaultMessage : 'I understand'
})}
>
})}>
<FormattedMessage
id='room.cookieConsent'
defaultMessage='This website uses cookies to enhance the user experience'
@ -193,7 +258,34 @@ const ChooseRoom = ({
ChooseRoom.propTypes =
{
roomClient : PropTypes.any.isRequired,
loginEnabled : PropTypes.bool.isRequired,
loggedIn : PropTypes.bool.isRequired,
myPicture : PropTypes.string,
classes : PropTypes.object.isRequired
};
export default withStyles(styles)(ChooseRoom);
const mapStateToProps = (state) =>
{
return {
loginEnabled : state.me.loginEnabled,
loggedIn : state.me.loggedIn,
myPicture : state.me.picture
};
};
export default withRoomContext(connect(
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.me.loginEnabled === next.me.loginEnabled &&
prev.me.loggedIn === next.me.loggedIn &&
prev.me.picture === next.me.picture
);
}
}
)(withStyles(styles)(ChooseRoom)));

View File

@ -1,12 +1,9 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { connect } from 'react-redux';
import {
meProducersSelector,
makePermissionSelector
} from '../Selectors';
import { permissions } from '../../permissions';
import { meProducersSelector } from '../Selectors';
import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
@ -14,7 +11,6 @@ import { useIntl, FormattedMessage } from 'react-intl';
import VideoView from '../VideoContainers/VideoView';
import Volume from './Volume';
import Fab from '@material-ui/core/Fab';
import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
@ -64,26 +60,33 @@ const styles = (theme) =>
margin : theme.spacing(1),
pointerEvents : 'auto'
},
smallContainer :
{
backgroundColor : 'rgba(255, 255, 255, 0.9)',
margin : '0.5vmin',
padding : '0.5vmin',
boxShadow : '0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12)',
pointerEvents : 'auto',
transition : 'background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
'&:hover' :
{
backgroundColor : 'rgba(213, 213, 213, 1)'
}
},
viewContainer :
{
position : 'relative',
width : '100%',
height : '100%'
},
meTag :
controls :
{
position : 'absolute',
width : '100%',
height : '100%',
backgroundColor : 'rgba(0, 0, 0, 0.3)',
display : 'flex',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'flex-end',
padding : theme.spacing(1),
zIndex : 21,
opacity : 0,
transition : 'opacity 0.3s',
touchAction : 'none',
pointerEvents : 'none',
'&.hover' :
{
opacity : 1
},
'& p' :
{
position : 'absolute',
float : 'left',
@ -92,64 +95,7 @@ const styles = (theme) =>
transform : 'translate(-50%, -50%)',
color : 'rgba(255, 255, 255, 0.5)',
fontSize : '7em',
zIndex : 30,
margin : 0,
opacity : 0,
transition : 'opacity 0.1s ease-in-out',
'&.hover' :
{
opacity : 1
},
'&.smallContainer' :
{
fontSize : '3em'
}
},
controls :
{
position : 'absolute',
width : '100%',
height : '100%',
display : 'flex',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'flex-end',
padding : theme.spacing(1),
zIndex : 21,
touchAction : 'none',
pointerEvents : 'none',
'&.hide' :
{
transition : 'opacity 0.1s ease-in-out',
opacity : 0
},
'&.hover' :
{
opacity : 1
}
},
ptt :
{
position : 'absolute',
float : 'left',
top : '25%',
left : '50%',
transform : 'translate(-50%, 0%)',
color : 'rgba(255, 255, 255, 0.7)',
fontSize : '1.3em',
backgroundColor : 'rgba(245, 0, 87, 0.70)',
margin : '4px',
padding : theme.spacing(2),
zIndex : 1200,
borderRadius : '20px',
textAlign : 'center',
opacity : 0,
transition : 'opacity 1s ease',
pointerEvents : 'none',
'&.enabled' :
{
transition : 'opacity 0.1s',
opacity : 1
margin : 0
}
}
});
@ -169,16 +115,13 @@ const Me = (props) =>
activeSpeaker,
spacing,
style,
smallContainer,
smallButtons,
advancedMode,
micProducer,
webcamProducer,
screenProducer,
extraVideoProducers,
canShareScreen,
transports,
noiseVolume,
classes
classes,
theme
} = props;
const videoVisible = (
@ -287,66 +230,13 @@ const Me = (props) =>
defaultMessage : 'Start screen sharing'
});
}
const [
screenShareTooltipOpen,
screenShareTooltipSetOpen
] = React.useState(false);
const screenShareTooltipHandleClose = () =>
{
screenShareTooltipSetOpen(false);
};
const screenShareTooltipHandleOpen = () =>
{
screenShareTooltipSetOpen(true);
};
if (screenState === 'off' && me.screenShareInProgress && screenShareTooltipOpen)
{
screenShareTooltipHandleClose();
}
const spacingStyle =
{
'margin' : spacing
};
let audioScore = null;
if (micProducer && micProducer.score)
{
audioScore =
micProducer.score.reduce(
(prev, curr) =>
(prev.score < curr.score ? prev : curr)
);
}
let videoScore = null;
if (webcamProducer && webcamProducer.score)
{
videoScore =
webcamProducer.score.reduce(
(prev, curr) =>
(prev.score < curr.score ? prev : curr)
);
}
useEffect(() =>
{
let poll;
const interval = 1000;
if (advancedMode)
{
poll = setInterval(() => roomClient.getTransportStats(), interval);
}
return () => clearInterval(poll);
}, [ roomClient, advancedMode ]);
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
return (
<React.Fragment>
@ -380,51 +270,9 @@ const Me = (props) =>
}}
style={spacingStyle}
>
{ me.browser.platform !== 'mobile' && smallContainer &&
<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 className={classes.viewContainer} style={style}>
{ me.browser.platform !== 'mobile' && !smallContainer &&
<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>
}
<p className={
classnames(
classes.meTag,
hover ? 'hover' : null,
smallContainer ? 'smallContainer' : null
)}
>
<FormattedMessage
id='room.me'
defaultMessage='ME'
/>
</p>
{ !settings.buttonControlBar &&
<div className={classnames(classes.viewContainer)} style={style}>
<div
className={classnames(
classes.controls,
settings.hiddenControls ? 'hide' : null,
hover ? 'hover' : null
)}
className={classnames(classes.controls, hover ? 'hover' : null)}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
onTouchStart={() =>
@ -445,45 +293,13 @@ const Me = (props) =>
}, 2000);
}}
>
<React.Fragment>
<Tooltip title={micTip} placement='left'>
{ smallContainer ?
<div>
<IconButton
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.smallContainer}
disabled={!me.canSendMic || me.audioInProgress}
color={
micState === 'on' ?
settings.voiceActivatedUnmute && !me.isAutoMuted ?
'primary'
: 'default'
: 'secondary'}
size='small'
onClick={() =>
{
if (micState === 'off')
roomClient.updateMic({ start: true });
else if (micState === 'on')
roomClient.muteMic();
else
roomClient.unmuteMic();
}}
>
{ micState === 'on' ?
<MicIcon
color={me.isAutoMuted ? 'secondary' : 'primary'}
style={{ opacity: noiseVolume }}
<p>
<FormattedMessage
id='room.me'
defaultMessage='ME'
/>
:
<MicOffIcon />
}
</IconButton>
</div>
:
</p>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
@ -492,15 +308,12 @@ const Me = (props) =>
})}
className={classes.fab}
disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ?
settings.voiceActivatedUnmute && !me.isAutoMuted? 'primary'
: 'default'
: 'secondary'}
size='large'
color={micState === 'on' ? 'default' : 'secondary'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
if (micState === 'off')
roomClient.updateMic({ start: true });
roomClient.enableMic();
else if (micState === 'on')
roomClient.muteMic();
else
@ -508,47 +321,14 @@ const Me = (props) =>
}}
>
{ micState === 'on' ?
<MicIcon
color={me.isAutoMuted && settings.voiceActivatedUnmute ?
'secondary' : 'primary'}
style={me.isAutoMuted && settings.voiceActivatedUnmute ?
{ opacity: noiseVolume }
: { opacity: 1 }}
/>
<MicIcon />
:
<MicOffIcon />
}
</Fab>
</div>
}
</Tooltip>
<Tooltip title={webcamTip} placement='left'>
{ smallContainer ?
<div>
<IconButton
aria-label={intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
})}
className={classes.smallContainer}
disabled={!me.canSendWebcam || me.webcamInProgress}
color={webcamState === 'on' ? 'primary' : 'secondary'}
size='small'
onClick={() =>
{
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.updateWebcam({ start: true });
}}
>
{ webcamState === 'on' ?
<VideoIcon />
:
<VideoOffIcon />
}
</IconButton>
</div>
:
<Tooltip title={webcamTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
@ -558,12 +338,12 @@ const Me = (props) =>
className={classes.fab}
disabled={!me.canSendWebcam || me.webcamInProgress}
color={webcamState === 'on' ? 'default' : 'secondary'}
size='large'
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.updateWebcam({ start: true });
roomClient.enableWebcam();
}}
>
{ webcamState === 'on' ?
@ -573,47 +353,8 @@ const Me = (props) =>
}
</Fab>
</div>
}
</Tooltip>
{ me.browser.platform !== 'mobile' &&
<Tooltip open={screenShareTooltipOpen}
onClose={screenShareTooltipHandleClose}
onOpen={screenShareTooltipHandleOpen}
title={screenTip} placement='left'
>
{ smallContainer ?
<div>
<IconButton
aria-label={intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
})}
className={classes.smallContainer}
disabled={
!canShareScreen ||
!me.canShareScreen ||
me.screenShareInProgress
}
color='primary'
size='small'
onClick={() =>
{
if (screenState === 'off')
roomClient.updateScreenSharing({ start: true });
else if (screenState === 'on')
roomClient.disableScreenSharing();
}}
>
{ (screenState === 'on' || screenState === 'unsupported') &&
<ScreenOffIcon/>
}
{ screenState === 'off' &&
<ScreenIcon/>
}
</IconButton>
</div>
:
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
@ -621,19 +362,28 @@ const Me = (props) =>
defaultMessage : 'Start screen sharing'
})}
className={classes.fab}
disabled={
!canShareScreen ||
!me.canShareScreen ||
me.screenShareInProgress
}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
size='large'
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
if (screenState === 'off')
roomClient.updateScreenSharing({ start: true });
else if (screenState === 'on')
switch (screenState)
{
case 'on':
{
roomClient.disableScreenSharing();
break;
}
case 'off':
{
roomClient.enableScreenSharing();
break;
}
default:
{
break;
}
}
}}
>
{ (screenState === 'on' || screenState === 'unsupported') &&
@ -644,16 +394,11 @@ const Me = (props) =>
}
</Fab>
</div>
}
</Tooltip>
}
</React.Fragment>
</div>
}
<VideoView
isMe
VideoView
advancedMode={advancedMode}
peer={me}
displayName={settings.displayName}
@ -662,156 +407,18 @@ const Me = (props) =>
videoVisible={videoVisible}
audioCodec={micProducer && micProducer.codec}
videoCodec={webcamProducer && webcamProducer.codec}
netInfo={transports && transports}
audioScore={audioScore}
videoScore={videoScore}
showQuality
onChangeDisplayName={(displayName) =>
{
roomClient.changeDisplayName(displayName);
}}
>
{ micState === 'muted' ? null : <Volume id={me.id} /> }
<Volume id={me.id} />
</VideoView>
</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}>
<p className={
classnames(
classes.meTag,
hover ? 'hover' : null,
smallContainer ? 'smallContainer' : null
)}
>
<FormattedMessage
id='room.me'
defaultMessage='ME'
/>
</p>
<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);
}}
>
<Tooltip title={webcamTip} placement='left'>
{ smallContainer ?
<div>
<IconButton
aria-label={intl.formatMessage({
id : 'device.stopVideo',
defaultMessage : 'Stop video'
})}
className={classes.smallContainer}
disabled={!me.canSendWebcam || me.webcamInProgress}
size='small'
color='primary'
onClick={() =>
{
roomClient.disableExtraVideo(producer.id);
}}
>
<VideoIcon />
</IconButton>
</div>
:
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.stopVideo',
defaultMessage : 'Stop video'
})}
className={classes.fab}
disabled={!me.canSendWebcam || me.webcamInProgress}
size={smallContainer ? 'small' : 'large'}
onClick={() =>
{
roomClient.disableExtraVideo(producer.id);
}}
>
<VideoIcon />
</Fab>
</div>
}
</Tooltip>
</div>
<VideoView
isMe
isExtraVideo
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 &&
<div
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
className={classnames(classes.root, 'screen', hover && 'hover')}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
onTouchStart={() =>
@ -833,19 +440,37 @@ const Me = (props) =>
}}
style={spacingStyle}
>
<div className={classes.viewContainer} style={style}>
<p className={
classnames(
classes.meTag,
hover ? 'hover' : null,
smallContainer ? 'smallContainer' : null
)}
<div className={classnames(classes.viewContainer)} style={style}>
<div
className={classnames(classes.controls, hover && 'hover')}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
onTouchStart={() =>
{
if (touchTimeout)
clearTimeout(touchTimeout);
setHover(true);
}}
onTouchEnd={() =>
{
if (touchTimeout)
clearTimeout(touchTimeout);
touchTimeout = setTimeout(() =>
{
setHover(false);
}, 2000);
}}
>
<p>
<FormattedMessage
id='room.me'
defaultMessage='ME'
/>
</p>
</div>
<VideoView
isMe
@ -873,66 +498,35 @@ Me.propTypes =
micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer,
extraVideoProducers : PropTypes.arrayOf(appPropTypes.Producer),
spacing : PropTypes.number,
style : PropTypes.object,
smallContainer : PropTypes.bool,
canShareScreen : PropTypes.bool.isRequired,
noiseVolume : PropTypes.number,
smallButtons : PropTypes.bool,
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired,
transports : PropTypes.object.isRequired
theme : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.SHARE_SCREEN);
const mapStateToProps = (state) =>
{
let volume;
// noiseVolume under threshold
if (state.peerVolumes[state.me.id] < state.settings.noiseThreshold)
{
// noiseVolume mapped to range 0.5 ... 1 (threshold switch)
volume = 1 + ((Math.abs(state.peerVolumes[state.me.id] -
state.settings.noiseThreshold) / (-120 -
state.settings.noiseThreshold)));
}
// noiseVolume over threshold: no noise but voice
else { volume = 0; }
return {
me : state.me,
...meProducersSelector(state),
settings : state.settings,
activeSpeaker : state.me.id === state.room.activeSpeakerId,
canShareScreen : hasPermission(state),
transports : state.transports,
noiseVolume : volume
activeSpeaker : state.me.id === state.room.activeSpeakerId
};
};
return mapStateToProps;
};
export default withRoomContext(connect(
makeMapStateToProps,
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me === next.me &&
Math.round(prev.peerVolumes[prev.me.id]) ===
Math.round(next.peerVolumes[next.me.id]) &&
prev.peers === next.peers &&
prev.producers === next.producers &&
prev.settings === next.settings &&
prev.transports === next.transports
prev.room.activeSpeakerId === next.room.activeSpeakerId
);
}
}

View File

@ -12,9 +12,8 @@ import { useIntl, FormattedMessage } from 'react-intl';
import VideoView from '../VideoContainers/VideoView';
import Tooltip from '@material-ui/core/Tooltip';
import Fab from '@material-ui/core/Fab';
import IconButton from '@material-ui/core/IconButton';
import VolumeUpIcon from '@material-ui/icons/VolumeUp';
import VolumeOffIcon from '@material-ui/icons/VolumeOff';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
import NewWindowIcon from '@material-ui/icons/OpenInNew';
import FullScreenIcon from '@material-ui/icons/Fullscreen';
import Volume from './Volume';
@ -60,19 +59,6 @@ const styles = (theme) =>
{
margin : theme.spacing(1)
},
smallContainer :
{
backgroundColor : 'rgba(255, 255, 255, 0.9)',
margin : '0.5vmin',
padding : '0.5vmin',
boxShadow : '0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12)',
pointerEvents : 'auto',
transition : 'background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
'&:hover' :
{
backgroundColor : 'rgba(213, 213, 213, 1)'
}
},
viewContainer :
{
position : 'relative',
@ -135,16 +121,14 @@ const Peer = (props) =>
advancedMode,
peer,
activeSpeaker,
browser,
micConsumer,
webcamConsumer,
screenConsumer,
extraVideoConsumers,
toggleConsumerFullscreen,
toggleConsumerWindow,
spacing,
style,
smallContainer,
smallButtons,
windowConsumer,
classes,
theme
@ -183,8 +167,8 @@ const Peer = (props) =>
classnames(
classes.root,
'webcam',
hover ? 'hover' : null,
activeSpeaker ? 'active-speaker' : null
hover && 'hover',
activeSpeaker && 'active-speaker'
)
}
onMouseOver={() => setHover(true)}
@ -221,7 +205,7 @@ const Peer = (props) =>
}
<div
className={classnames(classes.controls, hover ? 'hover' : null)}
className={classnames(classes.controls, hover && 'hover')}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
onTouchStart={() =>
@ -249,30 +233,7 @@ const Peer = (props) =>
})}
placement={smallScreen ? 'top' : 'left'}
>
{ smallContainer ?
<IconButton
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.smallContainer}
disabled={!micConsumer}
color='primary'
size='small'
onClick={() =>
{
micEnabled ?
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
}}
>
{ micEnabled ?
<VolumeUpIcon />
:
<VolumeOffIcon />
}
</IconButton>
:
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.muteAudio',
@ -281,7 +242,7 @@ const Peer = (props) =>
className={classes.fab}
disabled={!micConsumer}
color={micEnabled ? 'default' : 'secondary'}
size='large'
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
micEnabled ?
@ -290,15 +251,15 @@ const Peer = (props) =>
}}
>
{ micEnabled ?
<VolumeUpIcon />
<MicIcon />
:
<VolumeOffIcon />
<MicOffIcon />
}
</Fab>
}
</div>
</Tooltip>
{ browser.platform !== 'mobile' &&
{ !smallScreen &&
<Tooltip
title={intl.formatMessage({
id : 'label.newWindow',
@ -306,27 +267,7 @@ const Peer = (props) =>
})}
placement={smallScreen ? 'top' : 'left'}
>
{ smallContainer ?
<IconButton
aria-label={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
className={classes.smallContainer}
disabled={
!videoVisible ||
(windowConsumer === webcamConsumer.id)
}
size='small'
color='primary'
onClick={() =>
{
toggleConsumerWindow(webcamConsumer);
}}
>
<NewWindowIcon />
</IconButton>
:
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.newWindow',
@ -337,7 +278,7 @@ const Peer = (props) =>
!videoVisible ||
(windowConsumer === webcamConsumer.id)
}
size='large'
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerWindow(webcamConsumer);
@ -345,7 +286,7 @@ const Peer = (props) =>
>
<NewWindowIcon />
</Fab>
}
</div>
</Tooltip>
}
@ -356,24 +297,7 @@ const Peer = (props) =>
})}
placement={smallScreen ? 'top' : 'left'}
>
{ smallContainer ?
<IconButton
aria-label={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
className={classes.smallContainer}
disabled={!videoVisible}
size='small'
color='primary'
onClick={() =>
{
toggleConsumerFullscreen(webcamConsumer);
}}
>
<FullScreenIcon />
</IconButton>
:
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.fullscreen',
@ -381,7 +305,7 @@ const Peer = (props) =>
})}
className={classes.fab}
disabled={!videoVisible}
size='large'
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerFullscreen(webcamConsumer);
@ -389,12 +313,11 @@ const Peer = (props) =>
>
<FullScreenIcon />
</Fab>
}
</div>
</Tooltip>
</div>
<VideoView
showQuality
advancedMode={advancedMode}
peer={peer}
displayName={peer.displayName}
@ -416,7 +339,6 @@ const Peer = (props) =>
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
videoTrack={webcamConsumer && webcamConsumer.track}
videoVisible={videoVisible}
audioTrack={micConsumer && micConsumer.track}
audioCodec={micConsumer && micConsumer.codec}
videoCodec={webcamConsumer && webcamConsumer.codec}
audioScore={micConsumer ? micConsumer.score : null}
@ -427,202 +349,9 @@ const Peer = (props) =>
</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'}
>
{ smallContainer ?
<IconButton
aria-label={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
className={classes.smallContainer}
disabled={
!videoVisible ||
(windowConsumer === consumer.id)
}
size='small'
color='primary'
onClick={() =>
{
toggleConsumerWindow(consumer);
}}
>
<NewWindowIcon />
</IconButton>
:
<Fab
aria-label={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
className={classes.fab}
disabled={
!videoVisible ||
(windowConsumer === consumer.id)
}
size='large'
onClick={() =>
{
toggleConsumerWindow(consumer);
}}
>
<NewWindowIcon />
</Fab>
}
</Tooltip>
}
<Tooltip
title={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
placement={smallScreen ? 'top' : 'left'}
>
{ smallContainer ?
<IconButton
aria-label={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
className={classes.smallContainer}
disabled={!videoVisible}
size='small'
color='primary'
onClick={() =>
{
toggleConsumerFullscreen(consumer);
}}
>
<FullScreenIcon />
</IconButton>
:
<Fab
aria-label={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
className={classes.fab}
disabled={!videoVisible}
size='large'
onClick={() =>
{
toggleConsumerFullscreen(consumer);
}}
>
<FullScreenIcon />
</Fab>
}
</Tooltip>
</div>
<VideoView
showQuality
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 &&
<div
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
className={classnames(classes.root, 'screen', hover && 'hover')}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
onTouchStart={() =>
@ -656,7 +385,7 @@ const Peer = (props) =>
</div>
}
<div
className={classnames(classes.controls, hover ? 'hover' : null)}
className={classnames(classes.controls, hover && 'hover')}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
onTouchStart={() =>
@ -678,7 +407,7 @@ const Peer = (props) =>
}, 2000);
}}
>
{ browser.platform !== 'mobile' &&
{ !smallScreen &&
<Tooltip
title={intl.formatMessage({
id : 'label.newWindow',
@ -686,6 +415,7 @@ const Peer = (props) =>
})}
placement={smallScreen ? 'top' : 'left'}
>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.newWindow',
@ -696,7 +426,7 @@ const Peer = (props) =>
!screenVisible ||
(windowConsumer === screenConsumer.id)
}
size={smallContainer ? 'small' : 'large'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerWindow(screenConsumer);
@ -704,6 +434,7 @@ const Peer = (props) =>
>
<NewWindowIcon />
</Fab>
</div>
</Tooltip>
}
@ -714,6 +445,7 @@ const Peer = (props) =>
})}
placement={smallScreen ? 'top' : 'left'}
>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.fullscreen',
@ -721,7 +453,7 @@ const Peer = (props) =>
})}
className={classes.fab}
disabled={!screenVisible}
size={smallContainer ? 'small' : 'large'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerFullscreen(screenConsumer);
@ -729,10 +461,10 @@ const Peer = (props) =>
>
<FullScreenIcon />
</Fab>
</div>
</Tooltip>
</div>
<VideoView
showQuality
advancedMode={advancedMode}
videoContain
consumerSpatialLayers={
@ -757,7 +489,6 @@ const Peer = (props) =>
videoTrack={screenConsumer && screenConsumer.track}
videoVisible={screenVisible}
videoCodec={screenConsumer && screenConsumer.codec}
videoScore={screenConsumer ? screenConsumer.score : null}
/>
</div>
</div>
@ -774,13 +505,11 @@ Peer.propTypes =
micConsumer : appPropTypes.Consumer,
webcamConsumer : appPropTypes.Consumer,
screenConsumer : appPropTypes.Consumer,
extraVideoConsumers : PropTypes.arrayOf(appPropTypes.Consumer),
windowConsumer : PropTypes.string,
activeSpeaker : PropTypes.bool,
browser : PropTypes.object.isRequired,
spacing : PropTypes.number,
style : PropTypes.object,
smallContainer : PropTypes.bool,
smallButtons : PropTypes.bool,
toggleConsumerFullscreen : PropTypes.func.isRequired,
toggleConsumerWindow : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired,
@ -797,8 +526,7 @@ const makeMapStateToProps = (initialState, { id }) =>
peer : state.peers[id],
...getPeerConsumers(state, id),
windowConsumer : state.room.windowConsumer,
activeSpeaker : id === state.room.activeSpeakerId,
browser : state.me.browser
activeSpeaker : id === state.room.activeSpeakerId
};
};
@ -832,8 +560,7 @@ export default withRoomContext(connect(
prev.peers === next.peers &&
prev.consumers === next.consumers &&
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
prev.room.windowConsumer === next.room.windowConsumer &&
prev.me.browser === next.me.browser
prev.room.windowConsumer === next.room.windowConsumer
);
}
}

View File

@ -91,6 +91,16 @@ const SpeakerPeer = (props) =>
!screenConsumer.remotelyPaused
);
let videoProfile;
if (webcamConsumer)
videoProfile = webcamConsumer.profile;
let screenProfile;
if (screenConsumer)
screenProfile = screenConsumer.profile;
const spacingStyle =
{
'margin' : spacing
@ -124,27 +134,11 @@ const SpeakerPeer = (props) =>
peer={peer}
displayName={peer.displayName}
showPeerInfo
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}
videoTrack={webcamConsumer ? webcamConsumer.track : null}
videoVisible={videoVisible}
audioCodec={micConsumer && micConsumer.codec}
videoCodec={webcamConsumer && webcamConsumer.codec}
audioScore={micConsumer ? micConsumer.score : null}
videoScore={webcamConsumer ? webcamConsumer.score : null}
videoProfile={videoProfile}
audioCodec={micConsumer ? micConsumer.codec : null}
videoCodec={webcamConsumer ? webcamConsumer.codec : null}
>
<Volume id={peer.id} />
</VideoView>
@ -171,29 +165,10 @@ const SpeakerPeer = (props) =>
<VideoView
advancedMode={advancedMode}
videoContain
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}
videoTrack={screenConsumer ? screenConsumer.track : null}
videoVisible={screenVisible}
videoCodec={screenConsumer && screenConsumer.codec}
videoScore={screenConsumer ? screenConsumer.score : null}
videoProfile={screenProfile}
videoCodec={screenConsumer ? screenConsumer.codec : null}
/>
</div>
}

View File

@ -58,27 +58,27 @@ const styles = () =>
'&.level6' :
{
height : '60%',
backgroundColor : 'rgba(255, 165, 0, 0.65)'
backgroundColor : 'rgba(255, 0, 0, 0.65)'
},
'&.level7' :
{
height : '70%',
backgroundColor : 'rgba(255, 100, 0, 0.65)'
backgroundColor : 'rgba(255, 0, 0, 0.65)'
},
'&.level8' :
{
height : '80%',
backgroundColor : 'rgba(255, 60, 0, 0.65)'
backgroundColor : 'rgba(0, 0, 0, 0.65)'
},
'&.level9' :
{
height : '90%',
backgroundColor : 'rgba(255, 30, 0, 0.65)'
backgroundColor : 'rgba(0, 0, 0, 0.65)'
},
'&.level10' :
{
height : '100%',
backgroundColor : 'rgba(255, 0, 0, 0.65)'
backgroundColor : 'rgba(0, 0, 0, 0.65)'
}
},
volumeSmall :
@ -94,17 +94,17 @@ const styles = () =>
smallBar :
{
flex : '0 0 auto',
margin : '0.3rem',
backgroundSize : '75%',
backgroundRepeat : 'no-repeat',
backgroundColor : 'rgba(0, 0, 0, 1)',
cursor : 'pointer',
transitionProperty : 'opacity, background-color',
width : 3,
borderRadius : 2,
borderRadius : 6,
transitionDuration : '0.25s',
position : 'absolute',
top : '50%',
transform : 'translateY(-50%)',
bottom : 0,
'&.level0' : { height: 0 },
'&.level1' : { height: '0.2vh' },
'&.level2' : { height: '0.4vh' },
@ -148,17 +148,10 @@ Volume.propTypes =
const makeMapStateToProps = (initialState, props) =>
{
const mapStateToProps = (state) =>
{
if (state.peerVolumes[props.id]>state.settings.noiseThreshold)
{
return {
volume : Math.round((state.peerVolumes[props.id]+100) / 10)
volume : state.peerVolumes[props.id]
};
}
else
{
return { volume: 0 };
}
};
return mapStateToProps;

View File

@ -1,164 +0,0 @@
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 { 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 DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import Link from '@material-ui/core/Link';
import Button from '@material-ui/core/Button';
import Divider from '@material-ui/core/Divider';
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'
}
},
logo :
{
marginRight : 'auto'
},
link :
{
display : 'block',
textAlign : 'center',
marginBottom : theme.spacing(1)
},
divider :
{
marginBottom : theme.spacing(3)
}
});
const eduMeetUrl='https://edumeet.org';
const About = ({
aboutOpen,
handleCloseAbout,
classes
}) =>
{
return (
<Dialog
open={aboutOpen}
onClose={() => handleCloseAbout(false)}
classes={{
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>
<FormattedMessage
id='room.about'
defaultMessage='About'
/>
</DialogTitle>
<DialogContent dividers>
<DialogContentText paragraph>
Contributions to this work were made on behalf of the GÉANT
project, a project that has received funding from the
European Unions 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.
</DialogContentText>
<DialogContentText paragraph>
GÉANT Vereniging (Association) is registered with the
Chamber of Commerce in Amsterdam with registration number
40535155 and operates in the UK as a branch of GÉANT
Vereniging. Registered office: Hoekenrode 3, 1102BR
Amsterdam, The Netherlands. UK branch address: City House,
126-130 Hills Road, Cambridge CB2 1PQ, UK.
</DialogContentText>
<DialogContentText align='center' paragraph>
<Link href={eduMeetUrl} target='_blank' rel='noreferrer' color='secondary' variant='h6'>
{eduMeetUrl}
</Link>
</DialogContentText>
<DialogContentText align='center' variant='body2'>
<FormattedMessage
id='label.version'
defaultMessage='Version'
/>
:{` ${process.env.REACT_APP_VERSION}`}
</DialogContentText>
<Divider variant='middle' light className={classes.divider}/>
{
window.config.supportUrl
&&
<DialogContentText align='center' paragraph>
<span>Visit for more info: </span>
<Link href={window.config.supportUrl} target='_blank' rel='noreferrer' color='secondary'>
{ window.config.supportUrl }
</Link>
</DialogContentText>
}
<Link href={window.config.privacyUrl ? window.config.privacyUrl : 'privacy/privacy.html'} target='_blank' rel='noreferrer' color='secondary' className={classes.link}>
Data protection and Privacy Policy
</Link>
</DialogContent>
<DialogActions>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
<Button onClick={() => { handleCloseAbout(false); }} color='primary'>
<FormattedMessage
id='label.close'
defaultMessage='Close'
/>
</Button>
</DialogActions>
</Dialog>
);
};
About.propTypes =
{
roomClient : PropTypes.object.isRequired,
aboutOpen : PropTypes.bool.isRequired,
handleCloseAbout : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
aboutOpen : state.room.aboutOpen
});
const mapDispatchToProps = {
handleCloseAbout : roomActions.setAboutOpen
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room.aboutOpen === next.room.aboutOpen
);
}
}
)(withStyles(styles)(About)));

View File

@ -1,341 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { meProducersSelector } from '../Selectors';
import { withStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext';
import { useIntl } from 'react-intl';
import Fab from '@material-ui/core/Fab';
import Tooltip from '@material-ui/core/Tooltip';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
import VideoIcon from '@material-ui/icons/Videocam';
import VideoOffIcon from '@material-ui/icons/VideocamOff';
import ScreenIcon from '@material-ui/icons/ScreenShare';
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
const styles = (theme) =>
({
root :
{
position : 'fixed',
display : 'flex',
zIndex : 30,
[theme.breakpoints.up('md')] :
{
top : '50%',
transform : 'translate(0%, -50%)',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'center',
left : theme.spacing(1)
},
[theme.breakpoints.down('sm')] :
{
flexDirection : 'row',
bottom : theme.spacing(1),
left : '50%',
transform : 'translate(-50%, -0%)'
}
},
fab :
{
margin : theme.spacing(1)
},
show :
{
opacity : 1,
transition : 'opacity .5s'
},
hide :
{
opacity : 0,
transition : 'opacity .5s'
},
move :
{
left : '30vw',
top : '50%',
transform : 'translate(0%, -50%)',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'center',
[theme.breakpoints.down('lg')] :
{
left : '40vw'
},
[theme.breakpoints.down('md')] :
{
left : '50vw'
},
[theme.breakpoints.down('sm')] :
{
left : '70vw'
},
[theme.breakpoints.down('xs')] :
{
left : '90vw'
}
}
});
const ButtonControlBar = (props) =>
{
const intl = useIntl();
const {
roomClient,
toolbarsVisible,
hiddenControls,
drawerOverlayed,
toolAreaOpen,
me,
micProducer,
webcamProducer,
screenProducer,
classes,
theme
} = props;
let micState;
let micTip;
if (!me.canSendMic)
{
micState = 'unsupported';
micTip = intl.formatMessage({
id : 'device.audioUnsupported',
defaultMessage : 'Audio unsupported'
});
}
else if (!micProducer)
{
micState = 'off';
micTip = intl.formatMessage({
id : 'device.activateAudio',
defaultMessage : 'Activate audio'
});
}
else if (!micProducer.locallyPaused && !micProducer.remotelyPaused)
{
micState = 'on';
micTip = intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
});
}
else
{
micState = 'muted';
micTip = intl.formatMessage({
id : 'device.unMuteAudio',
defaultMessage : 'Unmute audio'
});
}
let webcamState;
let webcamTip;
if (!me.canSendWebcam)
{
webcamState = 'unsupported';
webcamTip = intl.formatMessage({
id : 'device.videoUnsupported',
defaultMessage : 'Video unsupported'
});
}
else if (webcamProducer)
{
webcamState = 'on';
webcamTip = intl.formatMessage({
id : 'device.stopVideo',
defaultMessage : 'Stop video'
});
}
else
{
webcamState = 'off';
webcamTip = intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
});
}
let screenState;
let screenTip;
if (!me.canShareScreen)
{
screenState = 'unsupported';
screenTip = intl.formatMessage({
id : 'device.screenSharingUnsupported',
defaultMessage : 'Screen sharing not supported'
});
}
else if (screenProducer)
{
screenState = 'on';
screenTip = intl.formatMessage({
id : 'device.stopScreenSharing',
defaultMessage : 'Stop screen sharing'
});
}
else
{
screenState = 'off';
screenTip = intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
});
}
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
return (
<div
className={
classnames(
classes.root,
hiddenControls ?
(toolbarsVisible ? classes.show : classes.hide) :
classes.show,
toolAreaOpen &&
(me.browser.platform !== 'mobile' && !drawerOverlayed) ?
classes.move : null
)
}
>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'right'}>
<Fab
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.fab}
disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ? 'default' : 'secondary'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
if (micState === 'off')
roomClient.updateMic({ start: true });
else if (micState === 'on')
roomClient.muteMic();
else
roomClient.unmuteMic();
}}
>
{ micState === 'on' ?
<MicIcon />
:
<MicOffIcon />
}
</Fab>
</Tooltip>
<Tooltip title={webcamTip} placement={smallScreen ? 'top' : 'right'}>
<Fab
aria-label={intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
})}
className={classes.fab}
disabled={!me.canSendWebcam || me.webcamInProgress}
color={webcamState === 'on' ? 'default' : 'secondary'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.updateWebcam({ start: true });
}}
>
{ webcamState === 'on' ?
<VideoIcon />
:
<VideoOffIcon />
}
</Fab>
</Tooltip>
{ me.browser.platform !== 'mobile' &&
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'right'}>
<Fab
aria-label={intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
})}
className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
if (screenState === 'off')
roomClient.updateScreenSharing({ start: true });
else if (screenState === 'on')
roomClient.disableScreenSharing();
}}
>
{ screenState === 'on' || screenState === 'unsupported' ?
<ScreenOffIcon/>
:null
}
{ screenState === 'off' ?
<ScreenIcon/>
:null
}
</Fab>
</Tooltip>
}
</div>
);
};
ButtonControlBar.propTypes =
{
roomClient : PropTypes.any.isRequired,
toolbarsVisible : PropTypes.bool.isRequired,
hiddenControls : PropTypes.bool.isRequired,
drawerOverlayed : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired,
me : appPropTypes.Me.isRequired,
micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer,
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
toolbarsVisible : state.room.toolbarsVisible,
hiddenControls : state.settings.hiddenControls,
drawerOverlayed : state.settings.drawerOverlayed,
toolAreaOpen : state.toolarea.toolAreaOpen,
...meProducersSelector(state),
me : state.me
});
export default withRoomContext(connect(
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
prev.settings.hiddenControls === next.settings.hiddenControls &&
prev.settings.drawerOverlayed === next.settings.drawerOverlayed &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen &&
prev.producers === next.producers &&
prev.me === next.me
);
}
}
)(withStyles(styles, { withTheme: true })(ButtonControlBar)));

View File

@ -1,167 +0,0 @@
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)));

View File

@ -1,167 +0,0 @@
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 Paper from '@material-ui/core/Paper';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
const shortcuts=[
{ key: 'h', label: 'room.help', defaultMessage: 'Help' },
{ key: 'm', label: 'device.muteAudio', defaultMessage: 'Mute Audio' },
{ key: 'v', label: 'device.stopVideo', defaultMessage: 'Mute Video' },
{ key: '1', label: 'label.democratic', defaultMessage: 'Democratic View' },
{ key: '2', label: 'label.filmstrip', defaultMessage: 'Filmstrip View' },
{ key: 'space', label: 'me.mutedPTT', defaultMessage: 'Push SPACE to talk' },
{ key: 'a', label: 'label.advanced', defaultMessage: 'Show advanced information' },
{ key: `${String.fromCharCode(8592)} ${String.fromCharCode(8594)}`, label: 'room.browsePeersSpotlight', defaultMessage: 'Browse participants into Spotlight' }
];
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'
},
display : 'flex',
flexDirection : 'column'
},
paper : {
padding : theme.spacing(1),
textAlign : 'center',
color : theme.palette.text.secondary,
whiteSpace : 'nowrap',
marginRight : theme.spacing(3),
marginBottom : theme.spacing(1),
minWidth : theme.spacing(8)
},
shortcuts : {
display : 'flex',
flexDirection : 'row',
alignItems : 'center',
paddingLeft : theme.spacing(2),
paddingRight : theme.spacing(2)
},
tabsHeader :
{
flexGrow : 1,
marginBottom : theme.spacing(1)
}
});
const Help = ({
helpOpen,
handleCloseHelp,
classes
}) =>
{
const intl = useIntl();
return (
<Dialog
open={helpOpen}
onClose={() => { handleCloseHelp(false); }}
classes={{
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>
<FormattedMessage
id='room.help'
defaultMessage='Help'
/>
</DialogTitle>
<Tabs
value={0}
className={classes.tabsHeader}
indicatorColor='primary'
textColor='primary'
variant='fullWidth'
>
<Tab
label={
intl.formatMessage({
id : 'room.shortcutKeys',
defaultMessage : 'Shortcut keys'
})
}
/>
</Tabs>
{shortcuts.map((value, index) =>
{
return (
<div key={index} className={classes.shortcuts}>
<Paper className={classes.paper}>
{value.key}
</Paper>
<FormattedMessage
id={value.label}
defaultMessage={value.defaultMessage}
/>
</div>
);
})}
<DialogActions>
<Button onClick={() => { handleCloseHelp(false); }} color='primary'>
<FormattedMessage
id='label.close'
defaultMessage='Close'
/>
</Button>
</DialogActions>
</Dialog>
);
};
Help.propTypes =
{
roomClient : PropTypes.object.isRequired,
helpOpen : PropTypes.bool.isRequired,
handleCloseHelp : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
helpOpen : state.room.helpOpen
});
const mapDispatchToProps = {
handleCloseHelp : roomActions.setHelpOpen
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room.helpOpen === next.room.helpOpen
);
}
}
)(withStyles(styles)(Help)));

View File

@ -1,32 +1,23 @@
import React, { useState } from 'react';
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
lobbyPeersKeySelector,
peersLengthSelector,
raisedHandsSelector,
makePermissionSelector
peersLengthSelector
} from '../Selectors';
import { permissions } from '../../permissions';
import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import * as roomActions from '../../actions/roomActions';
import * as toolareaActions from '../../actions/toolareaActions';
import { useIntl, FormattedMessage } from 'react-intl';
import classnames from 'classnames';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import Popover from '@material-ui/core/Popover';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import Avatar from '@material-ui/core/Avatar';
import Badge from '@material-ui/core/Badge';
import Paper from '@material-ui/core/Paper';
import ExtensionIcon from '@material-ui/icons/Extension';
import AccountCircle from '@material-ui/icons/AccountCircle';
import FullScreenIcon from '@material-ui/icons/Fullscreen';
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
@ -35,40 +26,11 @@ import SecurityIcon from '@material-ui/icons/Security';
import PeopleIcon from '@material-ui/icons/People';
import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import VideoCallIcon from '@material-ui/icons/VideoCall';
import Button from '@material-ui/core/Button';
import Tooltip from '@material-ui/core/Tooltip';
import MoreIcon from '@material-ui/icons/MoreVert';
import HelpIcon from '@material-ui/icons/Help';
import InfoIcon from '@material-ui/icons/Info';
const styles = (theme) =>
({
persistentDrawerOpen :
{
width : 'calc(100% - 30vw)',
marginLeft : '30vw',
[theme.breakpoints.down('lg')] :
{
width : 'calc(100% - 40vw)',
marginLeft : '40vw'
},
[theme.breakpoints.down('md')] :
{
width : 'calc(100% - 50vw)',
marginLeft : '50vw'
},
[theme.breakpoints.down('sm')] :
{
width : 'calc(100% - 70vw)',
marginLeft : '70vw'
},
[theme.breakpoints.down('xs')] :
{
width : 'calc(100% - 90vw)',
marginLeft : '90vw'
}
},
menuButton :
{
margin : 0,
@ -110,34 +72,14 @@ const styles = (theme) =>
display : 'block'
}
},
sectionDesktop : {
display : 'none',
[theme.breakpoints.up('md')] : {
actionButtons :
{
display : 'flex'
}
},
sectionMobile : {
display : 'flex',
[theme.breakpoints.up('md')] : {
display : 'none'
}
},
actionButton :
{
margin : theme.spacing(1, 0),
padding : theme.spacing(0, 1)
},
disabledButton :
{
margin : theme.spacing(1, 0)
},
green :
{
color : 'rgba(0, 153, 0, 1)'
},
moreAction :
{
margin : theme.spacing(0.5, 0, 0.5, 1.5)
}
});
@ -176,47 +118,12 @@ const TopBar = (props) =>
{
const intl = useIntl();
const [ mobileMoreAnchorEl, setMobileMoreAnchorEl ] = useState(null);
const [ anchorEl, setAnchorEl ] = useState(null);
const [ currentMenu, setCurrentMenu ] = useState(null);
const handleExited = () =>
{
setCurrentMenu(null);
};
const handleMobileMenuOpen = (event) =>
{
setMobileMoreAnchorEl(event.currentTarget);
};
const handleMobileMenuClose = () =>
{
setMobileMoreAnchorEl(null);
};
const handleMenuOpen = (event, menu) =>
{
setAnchorEl(event.currentTarget);
setCurrentMenu(menu);
};
const handleMenuClose = () =>
{
setAnchorEl(null);
handleMobileMenuClose();
};
const {
roomClient,
room,
peersLength,
lobbyPeers,
permanentTopBar,
drawerOverlayed,
toolAreaOpen,
isMobile,
myPicture,
loggedIn,
loginEnabled,
@ -224,22 +131,13 @@ const TopBar = (props) =>
fullscreen,
onFullscreen,
setSettingsOpen,
setExtraVideoOpen,
setHelpOpen,
setAboutOpen,
setLockDialogOpen,
toggleToolArea,
openUsersTab,
unread,
canProduceExtraVideo,
canLock,
canPromote,
classes
} = props;
const isMenuOpen = Boolean(anchorEl);
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
const lockTooltip = room.locked ?
intl.formatMessage({
id : 'tooltip.unLockRoom',
@ -274,15 +172,9 @@ const TopBar = (props) =>
});
return (
<React.Fragment>
<AppBar
position='fixed'
className={classnames(
room.toolbarsVisible || permanentTopBar ?
classes.show : classes.hide,
!(isMobile || drawerOverlayed) && toolAreaOpen ?
classes.persistentDrawerOpen : null
)}
className={room.toolbarsVisible || permanentTopBar ? classes.show : classes.hide}
>
<Toolbar>
<PulsingBadge
@ -301,36 +193,17 @@ const TopBar = (props) =>
<MenuIcon />
</IconButton>
</PulsingBadge>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
{ window.config && 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' }
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
</Typography>
<div className={classes.grow} />
<div className={classes.sectionDesktop}>
<Tooltip
title={intl.formatMessage({
id : 'label.moreActions',
defaultMessage : 'More actions'
})}
>
<IconButton
aria-owns={
isMenuOpen &&
currentMenu === 'moreActions' ?
'material-appbar' : undefined
}
aria-haspopup
onClick={(event) => handleMenuOpen(event, 'moreActions')}
color='inherit'
>
<ExtensionIcon />
</IconButton>
</Tooltip>
<div className={classes.actionButtons}>
{ fullscreenEnabled &&
<Tooltip title={fullscreenTooltip}>
<IconButton
@ -391,7 +264,6 @@ const TopBar = (props) =>
</IconButton>
</Tooltip>
<Tooltip title={lockTooltip}>
<span className={classes.disabledButton}>
<IconButton
aria-label={intl.formatMessage({
id : 'tooltip.lockRoom',
@ -399,7 +271,6 @@ const TopBar = (props) =>
})}
className={classes.actionButton}
color='inherit'
disabled={!canLock}
onClick={() =>
{
if (room.locked)
@ -418,7 +289,6 @@ const TopBar = (props) =>
<LockOpenIcon />
}
</IconButton>
</span>
</Tooltip>
{ lobbyPeers.length > 0 &&
<Tooltip
@ -427,15 +297,12 @@ const TopBar = (props) =>
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
@ -445,7 +312,6 @@ const TopBar = (props) =>
<SecurityIcon />
</PulsingBadge>
</IconButton>
</span>
</Tooltip>
}
{ loginEnabled &&
@ -465,49 +331,11 @@ const TopBar = (props) =>
{ myPicture ?
<Avatar src={myPicture} />
:
<AccountCircle className={loggedIn ? classes.green : null} />
<AccountCircle />
}
</IconButton>
</Tooltip>
}
</div>
<div className={classes.sectionMobile}>
{ 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>
}
<IconButton
aria-haspopup
onClick={handleMobileMenuOpen}
color='inherit'
>
<MoreIcon />
</IconButton>
</div>
<div className={classes.divider} />
<Button
aria-label={intl.formatMessage({
@ -524,245 +352,9 @@ const TopBar = (props) =>
defaultMessage='Leave'
/>
</Button>
</div>
</Toolbar>
</AppBar>
<Popover
anchorEl={anchorEl}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
open={isMenuOpen}
onClose={handleMenuClose}
onExited={handleExited}
getContentAnchorEl={null}
>
{ currentMenu === 'moreActions' &&
<Paper>
<MenuItem
disabled={!canProduceExtraVideo}
onClick={() =>
{
handleMenuClose();
setExtraVideoOpen(!room.extraVideoOpen);
}}
>
<VideoCallIcon
aria-label={intl.formatMessage({
id : 'label.addVideo',
defaultMessage : 'Add video'
})}
/>
<p className={classes.moreAction}>
<FormattedMessage
id='label.addVideo'
defaultMessage='Add video'
/>
</p>
</MenuItem>
<MenuItem
onClick={() =>
{
handleMenuClose();
setHelpOpen(!room.helpOpen);
}}
>
<HelpIcon
aria-label={intl.formatMessage({
id : 'room.help',
defaultMessage : 'Help'
})}
/>
<p className={classes.moreAction}>
<FormattedMessage
id='room.help'
defaultMessage='Help'
/>
</p>
</MenuItem>
<MenuItem
onClick={() =>
{
handleMenuClose();
setAboutOpen(!room.aboutOpen);
}}
>
<InfoIcon
aria-label={intl.formatMessage({
id : 'room.about',
defaultMessage : 'About'
})}
/>
<p className={classes.moreAction}>
<FormattedMessage
id='room.about'
defaultMessage='About'
/>
</p>
</MenuItem>
</Paper>
}
</Popover>
<Menu
anchorEl={mobileMoreAnchorEl}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'bottom', horizontal: 'right' }}
open={isMobileMenuOpen}
onClose={handleMenuClose}
getContentAnchorEl={null}
>
{ loginEnabled &&
<MenuItem
aria-label={loginTooltip}
onClick={() =>
{
handleMenuClose();
loggedIn ? roomClient.logout() : roomClient.login();
}}
>
{ myPicture ?
<Avatar src={myPicture} />
:
<AccountCircle className={loggedIn ? classes.green : null} />
}
{ loggedIn ?
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.logout'
defaultMessage='Log out'
/>
</p>
:
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.login'
defaultMessage='Log in'
/>
</p>
}
</MenuItem>
}
<MenuItem
aria-label={lockTooltip}
disabled={!canLock}
onClick={() =>
{
handleMenuClose();
if (room.locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
>
{ room.locked ?
<LockIcon />
:
<LockOpenIcon />
}
{ room.locked ?
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.unLockRoom'
defaultMessage='Unlock room'
/>
</p>
:
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.lockRoom'
defaultMessage='Lock room'
/>
</p>
}
</MenuItem>
<MenuItem
aria-label={intl.formatMessage({
id : 'tooltip.settings',
defaultMessage : 'Show settings'
})}
onClick={() =>
{
handleMenuClose();
setSettingsOpen(!room.settingsOpen);
}}
>
<SettingsIcon />
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.settings'
defaultMessage='Show settings'
/>
</p>
</MenuItem>
<MenuItem
aria-label={intl.formatMessage({
id : 'tooltip.participants',
defaultMessage : 'Show participants'
})}
onClick={() =>
{
handleMenuClose();
openUsersTab();
}}
>
<Badge
color='primary'
badgeContent={peersLength + 1}
>
<PeopleIcon />
</Badge>
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.participants'
defaultMessage='Show participants'
/>
</p>
</MenuItem>
{ fullscreenEnabled &&
<MenuItem
aria-label={intl.formatMessage({
id : 'tooltip.enterFullscreen',
defaultMessage : 'Enter fullscreen'
})}
onClick={() =>
{
handleMenuClose();
onFullscreen();
}}
>
{ fullscreen ?
<FullScreenExitIcon />
:
<FullScreenIcon />
}
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.enterFullscreen'
defaultMessage='Enter fullscreen'
/>
</p>
</MenuItem>
}
<MenuItem
aria-label={intl.formatMessage({
id : 'label.moreActions',
defaultMessage : 'Add video'
})}
onClick={(event) => handleMenuOpen(event, 'moreActions')}
>
<ExtensionIcon />
<p className={classes.moreAction}>
<FormattedMessage
id='label.moreActions'
defaultMessage='More actions'
/>
</p>
</MenuItem>
</Menu>
</React.Fragment>
);
};
@ -770,12 +362,9 @@ TopBar.propTypes =
{
roomClient : PropTypes.object.isRequired,
room : appPropTypes.Room.isRequired,
isMobile : PropTypes.bool.isRequired,
peersLength : PropTypes.number,
lobbyPeers : PropTypes.array,
permanentTopBar : PropTypes.bool.isRequired,
drawerOverlayed : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired,
permanentTopBar : PropTypes.bool,
myPicture : PropTypes.string,
loggedIn : PropTypes.bool.isRequired,
loginEnabled : PropTypes.bool.isRequired,
@ -784,53 +373,27 @@ TopBar.propTypes =
onFullscreen : PropTypes.func.isRequired,
setToolbarsVisible : PropTypes.func.isRequired,
setSettingsOpen : PropTypes.func.isRequired,
setExtraVideoOpen : PropTypes.func.isRequired,
setHelpOpen : PropTypes.func.isRequired,
setAboutOpen : PropTypes.func.isRequired,
setLockDialogOpen : PropTypes.func.isRequired,
toggleToolArea : PropTypes.func.isRequired,
openUsersTab : PropTypes.func.isRequired,
unread : PropTypes.number.isRequired,
canProduceExtraVideo : PropTypes.bool.isRequired,
canLock : PropTypes.bool.isRequired,
canPromote : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasExtraVideoPermission =
makePermissionSelector(permissions.EXTRA_VIDEO);
const hasLockPermission =
makePermissionSelector(permissions.CHANGE_ROOM_LOCK);
const hasPromotionPermission =
makePermissionSelector(permissions.PROMOTE_PEER);
const mapStateToProps = (state) =>
({
room : state.room,
isMobile : state.me.browser.platform === 'mobile',
peersLength : peersLengthSelector(state),
lobbyPeers : lobbyPeersKeySelector(state),
permanentTopBar : state.settings.permanentTopBar,
drawerOverlayed : state.settings.drawerOverlayed,
toolAreaOpen : state.toolarea.toolAreaOpen,
loggedIn : state.me.loggedIn,
loginEnabled : state.me.loginEnabled,
myPicture : state.me.picture,
unread : state.toolarea.unreadMessages +
state.toolarea.unreadFiles + raisedHandsSelector(state),
canProduceExtraVideo : hasExtraVideoPermission(state),
canLock : hasLockPermission(state),
canPromote : hasPromotionPermission(state)
state.toolarea.unreadFiles
});
return mapStateToProps;
};
const mapDispatchToProps = (dispatch) =>
({
setToolbarsVisible : (visible) =>
@ -839,23 +402,11 @@ const mapDispatchToProps = (dispatch) =>
},
setSettingsOpen : (settingsOpen) =>
{
dispatch(roomActions.setSettingsOpen(settingsOpen));
},
setExtraVideoOpen : (extraVideoOpen) =>
{
dispatch(roomActions.setExtraVideoOpen(extraVideoOpen));
},
setHelpOpen : (helpOpen) =>
{
dispatch(roomActions.setHelpOpen(helpOpen));
},
setAboutOpen : (aboutOpen) =>
{
dispatch(roomActions.setAboutOpen(aboutOpen));
dispatch(roomActions.setSettingsOpen({ settingsOpen }));
},
setLockDialogOpen : (lockDialogOpen) =>
{
dispatch(roomActions.setLockDialogOpen(lockDialogOpen));
dispatch(roomActions.setLockDialogOpen({ lockDialogOpen }));
},
toggleToolArea : () =>
{
@ -869,7 +420,7 @@ const mapDispatchToProps = (dispatch) =>
});
export default withRoomContext(connect(
makeMapStateToProps,
mapStateToProps,
mapDispatchToProps,
null,
{
@ -880,15 +431,11 @@ export default withRoomContext(connect(
prev.peers === next.peers &&
prev.lobbyPeers === next.lobbyPeers &&
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
prev.settings.drawerOverlayed === next.settings.drawerOverlayed &&
prev.me.loggedIn === next.me.loggedIn &&
prev.me.browser === next.me.browser &&
prev.me.loginEnabled === next.me.loginEnabled &&
prev.me.picture === next.me.picture &&
prev.me.roles === next.me.roles &&
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
prev.toolarea.unreadFiles === next.toolarea.unreadFiles
);
}
}

View File

@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../RoomContext';
import classnames from 'classnames';
import isElectron from 'is-electron';
import * as settingsActions from '../actions/settingsActions';
import PropTypes from 'prop-types';
@ -83,10 +82,6 @@ const styles = (theme) =>
green :
{
color : 'rgba(0, 153, 0, 1)'
},
red :
{
color : 'rgba(153, 0, 0, 1)'
}
});
@ -108,7 +103,7 @@ const DialogTitle = withStyles(styles)((props) =>
};
}, []);
const { children, classes, myPicture, onLogin, loggedIn, ...other } = props;
const { children, classes, myPicture, onLogin, ...other } = props;
const handleTooltipClose = () =>
{
@ -120,27 +115,19 @@ const DialogTitle = withStyles(styles)((props) =>
setOpen(true);
};
const loginTooltip = loggedIn ?
intl.formatMessage({
id : 'tooltip.logout',
defaultMessage : 'Log out'
})
:
intl.formatMessage({
id : 'tooltip.login',
defaultMessage : 'Log in'
});
return (
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
{ window.config && window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
<Typography variant='h5'>{children}</Typography>
{ window.config.loginEnabled &&
{ window.config && window.config.loginEnabled &&
<Tooltip
onClose={handleTooltipClose}
onOpen={handleTooltipOpen}
open={open}
title={loginTooltip}
title={intl.formatMessage({
id : 'tooltip.login',
defaultMessage : 'Click to log in'
})}
placement='left'
>
<IconButton
@ -152,9 +139,7 @@ const DialogTitle = withStyles(styles)((props) =>
{ myPicture ?
<Avatar src={myPicture} className={classes.largeAvatar} />
:
<AccountCircle
className={classnames(classes.largeIcon, loggedIn ? classes.green : null)}
/>
<AccountCircle className={classes.largeIcon} />
}
</IconButton>
</Tooltip>
@ -208,11 +193,10 @@ const JoinDialog = ({
myPicture={myPicture}
onLogin={() =>
{
loggedIn ? roomClient.logout(roomId) : roomClient.login(roomId);
loggedIn ? roomClient.logout() : roomClient.login();
}}
loggedIn={loggedIn}
>
{ window.config.title ? window.config.title : 'Multiparty meeting' }
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
<hr />
</DialogTitle>
<DialogContent>
@ -269,16 +253,6 @@ const JoinDialog = ({
}}
fullWidth
/>
{!room.inLobby && room.overRoomLimit &&
<DialogContentText className={classes.red} variant='h6' gutterBottom>
<FormattedMessage
id='room.overRoomLimit'
defaultMessage={
'The room is full, retry after some time.'
}
/>
</DialogContentText>
}
</DialogContent>
@ -317,7 +291,6 @@ const JoinDialog = ({
className={classes.green}
gutterBottom
variant='h6'
style={{ fontWeight: '600' }}
align='center'
>
<FormattedMessage
@ -326,11 +299,7 @@ const JoinDialog = ({
/>
</DialogContentText>
{ room.signInRequired ?
<DialogContentText
gutterBottom
variant='h5'
style={{ fontWeight: '600' }}
>
<DialogContentText gutterBottom>
<FormattedMessage
id='room.emptyRequireLogin'
defaultMessage={
@ -340,11 +309,7 @@ const JoinDialog = ({
/>
</DialogContentText>
:
<DialogContentText
gutterBottom
variant='h5'
style={{ fontWeight: '600' }}
>
<DialogContentText gutterBottom>
<FormattedMessage
id='room.locketWait'
defaultMessage='The room is locked - hang on until somebody lets you in ...'
@ -358,8 +323,7 @@ const JoinDialog = ({
<CookieConsent buttonText={intl.formatMessage({
id : 'room.consentUnderstand',
defaultMessage : 'I understand'
})}
>
})}>
<FormattedMessage
id='room.cookieConsent'
defaultMessage='This website uses cookies to enhance the user experience'
@ -417,7 +381,6 @@ export default withRoomContext(connect(
return (
prev.room.inLobby === next.room.inLobby &&
prev.room.signInRequired === next.room.signInRequired &&
prev.room.overRoomLimit === next.room.overRoomLimit &&
prev.settings.displayName === next.settings.displayName &&
prev.me.displayNameInProgress === next.me.displayNameInProgress &&
prev.me.loginEnabled === next.me.loginEnabled &&

View File

@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import ChatModerator from './ChatModerator';
import MessageList from './MessageList';
import ChatInput from './ChatInput';
@ -26,7 +25,6 @@ const Chat = (props) =>
return (
<Paper className={classes.root}>
<ChatModerator />
<MessageList />
<ChatInput />
</Paper>

View File

@ -4,8 +4,6 @@ import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import { permissions } from '../../../permissions';
import { makePermissionSelector } from '../../Selectors';
import Paper from '@material-ui/core/Paper';
import InputBase from '@material-ui/core/InputBase';
import IconButton from '@material-ui/core/IconButton';
@ -56,7 +54,6 @@ const ChatInput = (props) =>
roomClient,
displayName,
picture,
canChat,
classes
} = props;
@ -69,7 +66,6 @@ const ChatInput = (props) =>
defaultMessage : 'Enter chat message...'
})}
value={message || ''}
disabled={!canChat}
onChange={handleChange}
onKeyPress={(ev) =>
{
@ -93,7 +89,6 @@ const ChatInput = (props) =>
color='primary'
className={classes.iconButton}
aria-label='Send'
disabled={!canChat}
onClick={() =>
{
if (message && message !== '')
@ -117,36 +112,24 @@ ChatInput.propTypes =
roomClient : PropTypes.object.isRequired,
displayName : PropTypes.string,
picture : PropTypes.string,
canChat : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.SEND_CHAT);
const mapStateToProps = (state) =>
({
displayName : state.settings.displayName,
picture : state.me.picture,
canChat : hasPermission(state)
picture : state.me.picture
});
return mapStateToProps;
};
export default withRoomContext(
connect(
makeMapStateToProps,
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me.roles === next.me.roles &&
prev.peers === next.peers &&
prev.settings.displayName === next.settings.displayName &&
prev.me.picture === next.me.picture
);

View File

@ -1,108 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRoomContext } from '../../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import { useIntl, FormattedMessage } from 'react-intl';
import { permissions } from '../../../permissions';
import { makePermissionSelector } from '../../Selectors';
import Button from '@material-ui/core/Button';
const styles = (theme) =>
({
root :
{
display : 'flex',
padding : theme.spacing(1),
boxShadow : '0 2px 5px 2px rgba(0, 0, 0, 0.2)',
backgroundColor : 'rgba(255, 255, 255, 1)'
},
listheader :
{
padding : theme.spacing(1),
fontWeight : 'bolder'
},
actionButton :
{
marginLeft : 'auto'
}
});
const ChatModerator = (props) =>
{
const intl = useIntl();
const {
roomClient,
isChatModerator,
room,
classes
} = props;
if (!isChatModerator)
return null;
return (
<ul className={classes.root}>
<li className={classes.listheader}>
<FormattedMessage
id='room.moderatoractions'
defaultMessage='Moderator actions'
/>
</li>
<Button
aria-label={intl.formatMessage({
id : 'room.clearChat',
defaultMessage : 'Clear chat'
})}
className={classes.actionButton}
variant='contained'
color='secondary'
disabled={room.clearChatInProgress}
onClick={() => roomClient.clearChat()}
>
<FormattedMessage
id='room.clearChat'
defaultMessage='Clear chat'
/>
</Button>
</ul>
);
};
ChatModerator.propTypes =
{
roomClient : PropTypes.any.isRequired,
isChatModerator : PropTypes.bool,
room : PropTypes.object,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.MODERATE_CHAT);
const mapStateToProps = (state) =>
({
isChatModerator : hasPermission(state),
room : state.room
});
return mapStateToProps;
};
export default withRoomContext(connect(
makeMapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me === next.me &&
prev.peers === next.peers
);
}
}
)(withStyles(styles)(ChatModerator)));

View File

@ -6,7 +6,6 @@ import DOMPurify from 'dompurify';
import marked from 'marked';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import { useIntl } from 'react-intl';
const linkRenderer = new marked.Renderer();
@ -56,8 +55,6 @@ const styles = (theme) =>
const Message = (props) =>
{
const intl = useIntl();
const {
self,
picture,
@ -91,16 +88,7 @@ const Message = (props) =>
}
) }}
/>
<Typography variant='caption'>
{ self ?
intl.formatMessage({
id : 'room.me',
defaultMessage : 'Me'
})
:
name
} - {time}
</Typography>
<Typography variant='caption'>{self ? 'Me' : name} - {time}</Typography>
</div>
</Paper>
);

View File

@ -4,10 +4,7 @@ import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import { permissions } from '../../../permissions';
import { makePermissionSelector } from '../../Selectors';
import FileList from './FileList';
import FileSharingModerator from './FileSharingModerator';
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
@ -27,10 +24,6 @@ const styles = (theme) =>
button :
{
margin : theme.spacing(1)
},
shareButtonsWrapper :
{
display : 'flex'
}
});
@ -42,14 +35,12 @@ const FileSharing = (props) =>
{
if (event.target.files.length > 0)
{
await props.roomClient.shareFiles(event.target.files);
props.roomClient.shareFiles(event.target.files);
}
};
const {
canShareFiles,
browser,
canShare,
classes
} = props;
@ -64,61 +55,25 @@ const FileSharing = (props) =>
defaultMessage : 'File sharing not supported'
});
const buttonGalleryDescription = canShareFiles ?
intl.formatMessage({
id : 'label.shareGalleryFile',
defaultMessage : 'Share image'
})
:
intl.formatMessage({
id : 'label.fileSharingUnsupported',
defaultMessage : 'File sharing not supported'
});
return (
<Paper className={classes.root}>
<FileSharingModerator />
<div className={classes.shareButtonsWrapper} >
<input
className={classes.input}
type='file'
disabled={!canShare}
onChange={handleFileChange}
// Need to reset to be able to share same file twice
onClick={(e) => (e.target.value = null)}
id='share-files-button'
/>
<input
className={classes.input}
type='file'
disabled={!canShare}
onChange={handleFileChange}
accept='image/*'
id='share-files-gallery-button'
/>
<label htmlFor='share-files-button'>
<Button
variant='contained'
component='span'
className={classes.button}
disabled={!canShareFiles || !canShare}
disabled={!canShareFiles}
>
{buttonDescription}
</Button>
</label>
{
(browser.platform === 'mobile') && canShareFiles && canShare && <label htmlFor='share-files-gallery-button'>
<Button
variant='contained'
component='span'
className={classes.button}
disabled={!canShareFiles || !canShare}
>
{buttonGalleryDescription}
</Button>
</label>
}
</div>
<FileList />
</Paper>
);
@ -126,45 +81,19 @@ const FileSharing = (props) =>
FileSharing.propTypes = {
roomClient : PropTypes.any.isRequired,
browser : PropTypes.object.isRequired,
canShareFiles : PropTypes.bool.isRequired,
tabOpen : PropTypes.bool.isRequired,
canShare : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.SHARE_FILE);
const mapStateToProps = (state) =>
{
return {
canShareFiles : state.me.canShareFiles,
browser : state.me.browser,
tabOpen : state.toolarea.currentToolTab === 'files',
canShare : hasPermission(state)
tabOpen : state.toolarea.currentToolTab === 'files'
};
};
return mapStateToProps;
};
export default withRoomContext(connect(
makeMapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me.browser === next.me.browser &&
prev.me.roles === next.me.roles &&
prev.me.canShareFiles === next.me.canShareFiles &&
prev.peers === next.peers &&
prev.toolarea.currentToolTab === next.toolarea.currentToolTab
);
}
}
mapStateToProps
)(withStyles(styles)(FileSharing)));

View File

@ -1,108 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRoomContext } from '../../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import { useIntl, FormattedMessage } from 'react-intl';
import { permissions } from '../../../permissions';
import { makePermissionSelector } from '../../Selectors';
import Button from '@material-ui/core/Button';
const styles = (theme) =>
({
root :
{
display : 'flex',
padding : theme.spacing(1),
boxShadow : '0 2px 5px 2px rgba(0, 0, 0, 0.2)',
backgroundColor : 'rgba(255, 255, 255, 1)'
},
listheader :
{
padding : theme.spacing(1),
fontWeight : 'bolder'
},
actionButton :
{
marginLeft : 'auto'
}
});
const FileSharingModerator = (props) =>
{
const intl = useIntl();
const {
roomClient,
isFileSharingModerator,
room,
classes
} = props;
if (!isFileSharingModerator)
return null;
return (
<ul className={classes.root}>
<li className={classes.listheader}>
<FormattedMessage
id='room.moderatoractions'
defaultMessage='Moderator actions'
/>
</li>
<Button
aria-label={intl.formatMessage({
id : 'room.clearFileSharing',
defaultMessage : 'Clear files'
})}
className={classes.actionButton}
variant='contained'
color='secondary'
disabled={room.clearFileSharingInProgress}
onClick={() => roomClient.clearFileSharing()}
>
<FormattedMessage
id='room.clearFileSharing'
defaultMessage='Clear files'
/>
</Button>
</ul>
);
};
FileSharingModerator.propTypes =
{
roomClient : PropTypes.any.isRequired,
isFileSharingModerator : PropTypes.bool,
room : PropTypes.object,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.MODERATE_FILES);
const mapStateToProps = (state) =>
({
isFileSharingModerator : hasPermission(state),
room : state.room
});
return mapStateToProps;
};
export default withRoomContext(connect(
makeMapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me === next.me &&
prev.peers === next.peers
);
}
}
)(withStyles(styles)(FileSharingModerator)));

View File

@ -1,6 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { raisedHandsSelector } from '../Selectors';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import * as toolareaActions from '../../actions/toolareaActions';
@ -52,7 +51,6 @@ const MeetingDrawer = (props) =>
currentToolTab,
unreadMessages,
unreadFiles,
raisedHands,
closeDrawer,
setToolTab,
classes,
@ -95,14 +93,10 @@ const MeetingDrawer = (props) =>
}
/>
<Tab
label={
<Badge color='secondary' badgeContent={raisedHands}>
{intl.formatMessage({
label={intl.formatMessage({
id : 'label.participants',
defaultMessage : 'Participants'
})}
</Badge>
}
/>
</Tabs>
<IconButton onClick={closeDrawer}>
@ -122,21 +116,16 @@ MeetingDrawer.propTypes =
setToolTab : PropTypes.func.isRequired,
unreadMessages : PropTypes.number.isRequired,
unreadFiles : PropTypes.number.isRequired,
raisedHands : PropTypes.number.isRequired,
closeDrawer : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
{
return {
const mapStateToProps = (state) => ({
currentToolTab : state.toolarea.currentToolTab,
unreadMessages : state.toolarea.unreadMessages,
unreadFiles : state.toolarea.unreadFiles,
raisedHands : raisedHandsSelector(state)
};
};
unreadFiles : state.toolarea.unreadFiles
});
const mapDispatchToProps = {
setToolTab : toolareaActions.setToolTab
@ -152,8 +141,7 @@ export default connect(
return (
prev.toolarea.currentToolTab === next.toolarea.currentToolTab &&
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
prev.peers === next.peers
prev.toolarea.unreadFiles === next.toolarea.unreadFiles
);
}
}

View File

@ -1,55 +1,79 @@
import React from 'react';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import * as appPropTypes from '../../appPropTypes';
import { useIntl } from 'react-intl';
import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import PanIcon from '@material-ui/icons/PanTool';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
import HandIcon from '../../../images/icon-hand-white.svg';
const styles = (theme) =>
({
root :
{
padding : theme.spacing(1),
width : '100%',
overflow : 'hidden',
cursor : 'auto',
display : 'flex'
},
listPeer :
{
display : 'flex'
},
avatar :
{
borderRadius : '50%',
height : '2rem',
marginTop : theme.spacing(0.5)
height : '2rem'
},
peerInfo :
{
fontSize : '1rem',
border : 'none',
display : 'flex',
paddingLeft : theme.spacing(1),
flexGrow : 1,
alignItems : 'center'
},
buttons :
indicators :
{
padding : theme.spacing(1)
left : 0,
top : 0,
display : 'flex',
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'center',
transition : 'opacity 0.3s'
},
green :
icon :
{
color : 'rgba(0, 153, 0, 1)'
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 intl = useIntl();
const {
roomClient,
me,
settings,
classes
@ -58,46 +82,26 @@ const ListMe = (props) =>
const picture = me.picture || EmptyAvatar;
return (
<div className={classes.root}>
<li className={classes.root}>
<div className={classes.listPeer}>
<img alt='My avatar' className={classes.avatar} src={picture} />
<div className={classes.peerInfo}>
{settings.displayName}
</div>
<Tooltip
title={intl.formatMessage({
id : 'tooltip.raisedHand',
defaultMessage : 'Raise hand'
})}
placement='bottom'
>
<IconButton
aria-label={intl.formatMessage({
id : 'tooltip.raisedHand',
defaultMessage : 'Raise hand'
})}
className={
classnames(me.raisedHand ? classes.green : null, classes.buttons)
}
disabled={me.raisedHandInProgress}
color='primary'
onClick={(e) =>
{
e.stopPropagation();
roomClient.setRaisedHand(!me.raisedHand);
}}
>
<PanIcon />
</IconButton>
</Tooltip>
<div className={classes.indicators}>
{ me.raisedHand &&
<div className={classnames(classes.icon, 'raise-hand')} />
}
</div>
</div>
</li>
);
};
ListMe.propTypes =
{
roomClient : PropTypes.object.isRequired,
me : appPropTypes.Me.isRequired,
settings : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired
@ -108,7 +112,7 @@ const mapStateToProps = (state) => ({
settings : state.settings
});
export default withRoomContext(connect(
export default connect(
mapStateToProps,
null,
null,
@ -121,4 +125,4 @@ export default withRoomContext(connect(
);
}
}
)(withStyles(styles)(ListMe)));
)(withStyles(styles)(ListMe));

View File

@ -1,124 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import { withRoomContext } from '../../../RoomContext';
import { useIntl, FormattedMessage } from 'react-intl';
import Button from '@material-ui/core/Button';
const styles = (theme) =>
({
root :
{
padding : theme.spacing(1),
display : 'flex'
},
divider :
{
marginLeft : theme.spacing(2)
}
});
const ListModerator = (props) =>
{
const intl = useIntl();
const {
roomClient,
room,
classes
} = props;
return (
<div className={classes.root}>
<Button
aria-label={intl.formatMessage({
id : 'room.muteAll',
defaultMessage : 'Mute all'
})}
variant='contained'
color='secondary'
disabled={room.muteAllInProgress}
onClick={() => roomClient.muteAllPeers()}
>
<FormattedMessage
id='room.muteAll'
defaultMessage='Mute all'
/>
</Button>
<div className={classes.divider} />
<Button
aria-label={intl.formatMessage({
id : 'room.stopAllVideo',
defaultMessage : 'Stop all video'
})}
variant='contained'
color='secondary'
disabled={room.stopAllVideoInProgress}
onClick={() => roomClient.stopAllPeerVideo()}
>
<FormattedMessage
id='room.stopAllVideo'
defaultMessage='Stop all video'
/>
</Button>
<div className={classes.divider} />
<Button
aria-label={intl.formatMessage({
id : 'room.stopAllScreenSharing',
defaultMessage : 'Stop all screen sharing'
})}
variant='contained'
color='secondary'
disabled={room.stopAllScreenSharingInProgress}
onClick={() => roomClient.stopAllPeerScreenSharing()}
>
<FormattedMessage
id='room.stopAllScreenSharing'
defaultMessage='Stop all screen sharing'
/>
</Button>
<div className={classes.divider} />
<Button
aria-label={intl.formatMessage({
id : 'room.closeMeeting',
defaultMessage : 'Close meeting'
})}
variant='contained'
color='secondary'
disabled={room.closeMeetingInProgress}
onClick={() => roomClient.closeMeeting()}
>
<FormattedMessage
id='room.closeMeeting'
defaultMessage='Close meeting'
/>
</Button>
</div>
);
};
ListModerator.propTypes =
{
roomClient : PropTypes.any.isRequired,
room : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
room : state.room
});
export default withRoomContext(connect(
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room
);
}
}
)(withStyles(styles)(ListModerator)));

View File

@ -3,43 +3,41 @@ import { connect } from 'react-redux';
import { makePeerConsumerSelector } from '../../Selectors';
import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from '../../appPropTypes';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import { green } from '@material-ui/core/colors';
import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import VideocamIcon from '@material-ui/icons/Videocam';
import VideocamOffIcon from '@material-ui/icons/VideocamOff';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
import VolumeUpIcon from '@material-ui/icons/VolumeUp';
import VolumeOffIcon from '@material-ui/icons/VolumeOff';
import ScreenIcon from '@material-ui/icons/ScreenShare';
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
import ExitIcon from '@material-ui/icons/ExitToApp';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
import PanIcon from '@material-ui/icons/PanTool';
import RecordVoiceOverIcon from '@material-ui/icons/RecordVoiceOver';
import HandIcon from '../../../images/icon-hand-white.svg';
const styles = (theme) =>
({
root :
{
padding : theme.spacing(1),
width : '100%',
overflow : 'hidden',
cursor : 'auto',
display : 'flex'
},
listPeer :
{
display : 'flex'
},
avatar :
{
borderRadius : '50%',
height : '2rem',
marginTop : theme.spacing(0.5)
height : '2rem'
},
peerInfo :
{
fontSize : '1rem',
border : 'none',
display : 'flex',
paddingLeft : theme.spacing(1),
flexGrow : 1,
@ -47,17 +45,86 @@ const styles = (theme) =>
},
indicators :
{
left : 0,
top : 0,
display : 'flex',
padding : theme.spacing(1)
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'center',
transition : 'opacity 0.3s'
},
buttons :
icon :
{
padding : theme.spacing(1)
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
},
green :
'&.on' :
{
color : 'rgba(0, 153, 0, 1)',
marginLeft : theme.spacing(2)
opacity : 1
},
'&.off' :
{
opacity : 0.2
},
'&.raise-hand' :
{
backgroundImage : `url(${HandIcon})`
}
},
controls :
{
float : 'right',
display : 'flex',
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'center'
},
button :
{
flex : '0 0 auto',
margin : '0.3rem',
borderRadius : 2,
backgroundColor : 'rgba(0, 0, 0, 0.5)',
cursor : 'pointer',
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
},
'&.unsupported' :
{
pointerEvents : 'none'
},
'&.disabled' :
{
pointerEvents : 'none',
backgroundColor : 'var(--media-control-botton-disabled)'
},
'&.on' :
{
backgroundColor : 'var(--media-control-botton-on)'
},
'&.off' :
{
backgroundColor : 'var(--media-control-botton-off)'
}
}
});
@ -67,22 +134,13 @@ const ListPeer = (props) =>
const {
roomClient,
isModerator,
spotlight,
peer,
micConsumer,
webcamConsumer,
screenConsumer,
children,
classes
} = props;
const webcamEnabled = (
Boolean(webcamConsumer) &&
!webcamConsumer.locallyPaused &&
!webcamConsumer.remotelyPaused
);
const micEnabled = (
Boolean(micConsumer) &&
!micConsumer.locallyPaused &&
@ -104,38 +162,22 @@ const ListPeer = (props) =>
<div className={classes.peerInfo}>
{peer.displayName}
</div>
{ peer.raisedHand &&
<IconButton
className={classes.buttons}
style={{ color: green[500] }}
disabled={!isModerator || peer.raisedHandInProgress}
onClick={(e) =>
{
e.stopPropagation();
roomClient.lowerPeerHand(peer.id);
}}
>
<PanIcon />
</IconButton>
<div className={classes.indicators}>
{ peer.raiseHandState &&
<div className={
classnames(
classes.icon, 'raise-hand', {
on : peer.raiseHandState,
off : !peer.raiseHandState
}
{ spotlight &&
<IconButton
className={classes.buttons}
style={{ color: green[500] }}
disabled
>
<RecordVoiceOverIcon />
</IconButton>
)
}
{ screenConsumer && spotlight &&
<Tooltip
title={intl.formatMessage({
id : 'tooltip.muteScreenSharing',
defaultMessage : 'Mute participant share'
})}
placement='bottom'
>
/>
}
</div>
{children}
<div className={classes.controls}>
{ screenConsumer &&
<IconButton
aria-label={intl.formatMessage({
id : 'tooltip.muteScreenSharing',
@ -143,11 +185,9 @@ const ListPeer = (props) =>
})}
color={ screenVisible ? 'primary' : 'secondary'}
disabled={ peer.peerScreenInProgress }
className={classes.buttons}
onClick={(e) =>
{
e.stopPropagation();
screenVisible ?
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
@ -159,48 +199,7 @@ const ListPeer = (props) =>
<ScreenOffIcon />
}
</IconButton>
</Tooltip>
}
{ spotlight &&
<Tooltip
title={intl.formatMessage({
id : 'tooltip.muteParticipantVideo',
defaultMessage : 'Mute participant video'
})}
placement='bottom'
>
<IconButton
aria-label={intl.formatMessage({
id : 'tooltip.muteParticipantVideo',
defaultMessage : 'Mute participant video'
})}
color={webcamEnabled ? 'primary' : 'secondary'}
disabled={peer.peerVideoInProgress}
className={classes.buttons}
onClick={(e) =>
{
e.stopPropagation();
webcamEnabled ?
roomClient.modifyPeerConsumer(peer.id, 'webcam', true) :
roomClient.modifyPeerConsumer(peer.id, 'webcam', false);
}}
>
{ webcamEnabled ?
<VideocamIcon />
:
<VideocamOffIcon />
}
</IconButton>
</Tooltip>
}
<Tooltip
title={intl.formatMessage({
id : 'tooltip.muteParticipant',
defaultMessage : 'Mute participant'
})}
placement='bottom'
>
<IconButton
aria-label={intl.formatMessage({
id : 'tooltip.muteParticipant',
@ -208,132 +207,21 @@ const ListPeer = (props) =>
})}
color={ micEnabled ? 'primary' : 'secondary'}
disabled={ peer.peerAudioInProgress }
className={classes.buttons}
onClick={(e) =>
{
e.stopPropagation();
micEnabled ?
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
}}
>
{ micEnabled ?
<VolumeUpIcon />
:
<VolumeOffIcon />
}
</IconButton>
</Tooltip>
{ isModerator &&
<Tooltip
title={intl.formatMessage({
id : 'tooltip.kickParticipant',
defaultMessage : 'Kick out participant'
})}
placement='bottom'
>
<IconButton
aria-label={intl.formatMessage({
id : 'tooltip.kickParticipant',
defaultMessage : 'Kick out participant'
})}
disabled={peer.peerKickInProgress}
className={classes.buttons}
color='secondary'
onClick={(e) =>
{
e.stopPropagation();
roomClient.kickPeer(peer.id);
}}
>
<ExitIcon />
</IconButton>
</Tooltip>
}
{ isModerator && micConsumer &&
<Tooltip
title={intl.formatMessage({
id : 'tooltip.muteParticipantAudioModerator',
defaultMessage : 'Mute participant audio globally'
})}
placement='bottom'
>
<IconButton
className={classes.buttons}
style={{ color: green[500] }}
disabled={!isModerator || peer.stopPeerAudioInProgress}
onClick={(e) =>
{
e.stopPropagation();
roomClient.mutePeer(peer.id);
}}
>
{ !micConsumer.remotelyPaused ?
<MicIcon />
:
<MicOffIcon />
}
</IconButton>
</Tooltip>
}
{ isModerator && webcamConsumer &&
<Tooltip
title={intl.formatMessage({
id : 'tooltip.muteParticipantVideoModerator',
defaultMessage : 'Mute participant video globally'
})}
placement='bottom'
>
<IconButton
className={classes.buttons}
style={{ color: green[500] }}
disabled={!isModerator || peer.stopPeerVideoInProgress}
onClick={(e) =>
{
e.stopPropagation();
roomClient.stopPeerVideo(peer.id);
}}
>
{ !webcamConsumer.remotelyPaused ?
<VideocamIcon />
:
<VideocamOffIcon />
}
</IconButton>
</Tooltip>
}
{ isModerator && screenConsumer &&
<Tooltip
title={intl.formatMessage({
id : 'tooltip.muteScreenSharingModerator',
defaultMessage : 'Mute participant screen share globally'
})}
placement='bottom'
>
<IconButton
className={classes.buttons}
style={{ color: green[500] }}
disabled={!isModerator || peer.stopPeerScreenSharingInProgress}
onClick={(e) =>
{
e.stopPropagation();
roomClient.stopPeerScreenSharing(peer.id);
}}
>
{ !screenConsumer.remotelyPaused ?
<ScreenIcon />
:
<ScreenOffIcon />
}
</IconButton>
</Tooltip>
}
{children}
</div>
</div>
);
};
@ -342,8 +230,6 @@ ListPeer.propTypes =
{
roomClient : PropTypes.any.isRequired,
advancedMode : PropTypes.bool,
isModerator : PropTypes.bool,
spotlight : PropTypes.bool,
peer : appPropTypes.Peer.isRequired,
micConsumer : appPropTypes.Consumer,
webcamConsumer : appPropTypes.Consumer,

View File

@ -1,19 +1,16 @@
import React from 'react';
import { connect } from 'react-redux';
import {
participantListSelector,
makePermissionSelector
passivePeersSelector,
spotlightPeersSelector
} from '../../Selectors';
import { permissions } from '../../../permissions';
import classnames from 'classnames';
import classNames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import PropTypes from 'prop-types';
import { Flipper, Flipped } from 'react-flip-toolkit';
import { FormattedMessage } from 'react-intl';
import ListPeer from './ListPeer';
import ListMe from './ListMe';
import ListModerator from './ListModerator';
import Volume from '../../Containers/Volume';
const styles = (theme) =>
@ -33,10 +30,12 @@ const styles = (theme) =>
},
listheader :
{
padding : theme.spacing(1),
fontWeight : 'bolder'
},
listItem :
{
padding : theme.spacing(1),
width : '100%',
overflow : 'hidden',
cursor : 'pointer',
@ -77,26 +76,14 @@ class ParticipantList extends React.PureComponent
const {
roomClient,
advancedMode,
isModerator,
participants,
spotlights,
passivePeers,
selectedPeerId,
spotlightPeers,
classes
} = this.props;
return (
<div className={classes.root} ref={(node) => { this.node = node; }}>
{ isModerator &&
<ul className={classes.list}>
<li className={classes.listheader}>
<FormattedMessage
id='room.moderatoractions'
defaultMessage='Moderator actions'
/>
</li>
<ListModerator />
</ul>
}
<ul className={classes.list}>
<li className={classes.listheader}>
<FormattedMessage
@ -109,42 +96,42 @@ class ParticipantList extends React.PureComponent
<ul className={classes.list}>
<li className={classes.listheader}>
<FormattedMessage
id='label.participants'
defaultMessage='Participants'
id='room.spotlights'
defaultMessage='Participants in Spotlight'
/>
</li>
<Flipper
flipKey={participants}
>
{ participants.map((peer) => (
<Flipped key={peer.id} flipId={peer.id}>
{ spotlightPeers.map((peerId) => (
<li
key={peer.id}
className={classnames(classes.listItem, {
selected : peer.id === selectedPeerId
key={peerId}
className={classNames(classes.listItem, {
selected : peerId === selectedPeerId
})}
onClick={() => roomClient.setSelectedPeer(peer.id)}
onClick={() => roomClient.setSelectedPeer(peerId)}
>
{ spotlights.includes(peer.id) ?
<ListPeer
id={peer.id}
advancedMode={advancedMode}
isModerator={isModerator}
spotlight
>
<Volume small id={peer.id} />
<ListPeer id={peerId} advancedMode={advancedMode}>
<Volume small id={peerId} />
</ListPeer>
:
<ListPeer
id={peer.id}
advancedMode={advancedMode}
isModerator={isModerator}
/>
}
</li>
</Flipped>
))}
</Flipper>
</ul>
<ul className={classes.list}>
<li className={classes.listheader}>
<FormattedMessage
id='room.passive'
defaultMessage='Passive Participants'
/>
</li>
{ passivePeers.map((peerId) => (
<li
key={peerId}
className={classNames(classes.listItem, {
selected : peerId === selectedPeerId
})}
onClick={() => roomClient.setSelectedPeer(peerId)}
>
<ListPeer id={peerId} advancedMode={advancedMode} />
</li>
))}
</ul>
</div>
);
@ -155,41 +142,32 @@ ParticipantList.propTypes =
{
roomClient : PropTypes.any.isRequired,
advancedMode : PropTypes.bool,
isModerator : PropTypes.bool,
participants : PropTypes.array,
spotlights : PropTypes.array,
passivePeers : PropTypes.array,
selectedPeerId : PropTypes.string,
spotlightPeers : PropTypes.array,
classes : PropTypes.object.isRequired
};
const makeMapStateToProps = () =>
{
const hasPermission = makePermissionSelector(permissions.MODERATE_ROOM);
const mapStateToProps = (state) =>
{
return {
isModerator : hasPermission(state),
participants : participantListSelector(state),
spotlights : state.room.spotlights,
selectedPeerId : state.room.selectedPeerId
passivePeers : passivePeersSelector(state),
selectedPeerId : state.room.selectedPeerId,
spotlightPeers : spotlightPeersSelector(state)
};
};
return mapStateToProps;
};
const ParticipantListContainer = withRoomContext(connect(
makeMapStateToProps,
mapStateToProps,
null,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.me.roles === next.me.roles &&
prev.peers === next.peers
prev.peers === next.peers &&
prev.room.spotlights === next.room.spotlights &&
prev.room.selectedPeerId === next.room.selectedPeerId
);
}
}

View File

@ -11,9 +11,10 @@ import Peer from '../Containers/Peer';
import Me from '../Containers/Me';
const RATIO = 1.334;
const PADDING = 60;
const PADDING_V = 50;
const PADDING_H = 0;
const styles = (theme) =>
const styles = () =>
({
root :
{
@ -22,7 +23,6 @@ const styles = (theme) =>
display : 'flex',
flexDirection : 'row',
flexWrap : 'wrap',
overflow : 'hidden',
justifyContent : 'center',
alignItems : 'center',
alignContent : 'center'
@ -36,14 +36,6 @@ const styles = (theme) =>
{
paddingTop : 60,
transition : 'padding .5s'
},
buttonControlBar :
{
paddingLeft : 60,
[theme.breakpoints.down('sm')] :
{
paddingLeft : 0
}
}
});
@ -74,11 +66,9 @@ class Democratic extends React.PureComponent
return;
}
const width =
this.peersRef.current.clientWidth - (this.props.buttonControlBar ? PADDING : 0);
const height =
this.peersRef.current.clientHeight -
(this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING : 0);
const width = this.peersRef.current.clientWidth - PADDING_H;
const height = this.peersRef.current.clientHeight -
(this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING_V : PADDING_H);
let x, y, space;
@ -140,7 +130,6 @@ class Democratic extends React.PureComponent
spotlightsPeers,
toolbarsVisible,
permanentTopBar,
buttonControlBar,
classes
} = this.props;
@ -154,9 +143,7 @@ class Democratic extends React.PureComponent
<div
className={classnames(
classes.root,
toolbarsVisible || permanentTopBar ?
classes.showingToolBar : classes.hiddenToolBar,
buttonControlBar ? classes.buttonControlBar : null
toolbarsVisible || permanentTopBar ? classes.showingToolBar : classes.hiddenToolBar
)}
ref={this.peersRef}
>
@ -188,9 +175,7 @@ Democratic.propTypes =
boxes : PropTypes.number,
spotlightsPeers : PropTypes.array.isRequired,
toolbarsVisible : PropTypes.bool.isRequired,
permanentTopBar : PropTypes.bool.isRequired,
buttonControlBar : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired,
permanentTopBar : PropTypes.bool,
classes : PropTypes.object.isRequired
};
@ -200,9 +185,7 @@ const mapStateToProps = (state) =>
boxes : videoBoxesSelector(state),
spotlightsPeers : spotlightPeersSelector(state),
toolbarsVisible : state.room.toolbarsVisible,
permanentTopBar : state.settings.permanentTopBar,
buttonControlBar : state.settings.buttonControlBar,
toolAreaOpen : state.toolarea.toolAreaOpen
permanentTopBar : state.settings.permanentTopBar
};
};
@ -219,10 +202,8 @@ export default connect(
prev.consumers === next.consumers &&
prev.room.spotlights === next.room.spotlights &&
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
prev.settings.buttonControlBar === next.settings.buttonControlBar &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
prev.settings.permanentTopBar === next.settings.permanentTopBar
);
}
}
)(withStyles(styles, { withTheme: true })(Democratic));
)(withStyles(styles)(Democratic));

View File

@ -12,12 +12,6 @@ import Peer from '../Containers/Peer';
import SpeakerPeer from '../Containers/SpeakerPeer';
import Grid from '@material-ui/core/Grid';
const RATIO = 1.334;
const PADDING_V = 40;
const PADDING_H = 0;
const FILMSTRING_PADDING_V = 10;
const FILMSTRING_PADDING_H = 0;
const styles = () =>
({
root :
@ -25,24 +19,25 @@ const styles = () =>
height : '100%',
width : '100%',
display : 'grid',
overflow : 'hidden',
gridTemplateColumns : '1fr',
gridTemplateRows : '1fr 0.25fr'
gridTemplateRows : '1.6fr minmax(0, 0.4fr)'
},
speaker :
{
gridArea : '1 / 1 / 1 / 1',
gridArea : '1 / 1 / 2 / 2',
display : 'flex',
justifyContent : 'center',
alignItems : 'center'
alignItems : 'center',
paddingTop : 40
},
filmStrip :
{
gridArea : '2 / 1 / 2 / 1'
gridArea : '2 / 1 / 3 / 2'
},
filmItem :
{
display : 'flex',
marginLeft : '6px',
border : 'var(--peer-border)',
'&.selected' :
{
@ -50,18 +45,8 @@ const styles = () =>
},
'&.active' :
{
borderColor : 'var(--selected-peer-border-color)'
opacity : '0.6'
}
},
hiddenToolBar :
{
paddingTop : 0,
transition : 'padding .5s'
},
showingToolBar :
{
paddingTop : 60,
transition : 'padding .5s'
}
});
@ -73,8 +58,6 @@ class Filmstrip extends React.PureComponent
this.resizeTimeout = null;
this.rootContainer = React.createRef();
this.activePeerContainer = React.createRef();
this.filmStripContainer = React.createRef();
@ -122,38 +105,24 @@ class Filmstrip extends React.PureComponent
{
const newState = {};
const root = this.rootContainer.current;
if (!root)
return;
const availableWidth = root.clientWidth;
// Grid is:
// 4/5 speaker
// 1/5 filmstrip
const availableSpeakerHeight = (root.clientHeight * 0.8) -
(this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING_V : PADDING_H);
const availableFilmstripHeight = root.clientHeight * 0.2;
const speaker = this.activePeerContainer.current;
if (speaker)
{
let speakerWidth = (availableWidth - PADDING_H);
let speakerWidth = (speaker.clientWidth - 100);
let speakerHeight = speakerWidth / RATIO;
let speakerHeight = (speakerWidth / 4) * 3;
if (this.isSharingCamera(this.getActivePeerId()))
{
speakerWidth /= 2;
speakerHeight = speakerWidth / RATIO;
speakerHeight = (speakerWidth / 4) * 3;
}
if (speakerHeight > (availableSpeakerHeight - PADDING_V))
if (speakerHeight > (speaker.clientHeight - 60))
{
speakerHeight = (availableSpeakerHeight - PADDING_V);
speakerWidth = speakerHeight * RATIO;
speakerHeight = (speaker.clientHeight - 60);
speakerWidth = (speakerHeight / 3) * 4;
}
newState.speakerWidth = speakerWidth;
@ -164,18 +133,14 @@ class Filmstrip extends React.PureComponent
if (filmStrip)
{
let filmStripHeight = availableFilmstripHeight - FILMSTRING_PADDING_V;
let filmStripHeight = filmStrip.clientHeight - 10;
let filmStripWidth = filmStripHeight * RATIO;
let filmStripWidth = (filmStripHeight / 3) * 4;
if (
(filmStripWidth * this.props.boxes) >
(availableWidth - FILMSTRING_PADDING_H)
)
if (filmStripWidth * this.props.boxes > (filmStrip.clientWidth - 50))
{
filmStripWidth = (availableWidth - FILMSTRING_PADDING_H) /
this.props.boxes;
filmStripHeight = filmStripWidth / RATIO;
filmStripWidth = (filmStrip.clientWidth - 50) / this.props.boxes;
filmStripHeight = (filmStripWidth / 4) * 3;
}
newState.filmStripWidth = filmStripWidth;
@ -207,21 +172,27 @@ class Filmstrip extends React.PureComponent
window.removeEventListener('resize', this.updateDimensions);
}
componentDidUpdate(prevProps)
componentWillUpdate(nextProps)
{
if (prevProps !== this.props)
if (nextProps !== this.props)
{
if (
this.props.activeSpeakerId != null &&
this.props.activeSpeakerId !== this.props.myId
nextProps.activeSpeakerId != null &&
nextProps.activeSpeakerId !== this.props.myId
)
{
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
lastSpeaker : this.props.activeSpeakerId
lastSpeaker : nextProps.activeSpeakerId
});
}
}
}
componentDidUpdate(prevProps)
{
if (prevProps !== this.props)
{
this.updateDimensions();
}
}
@ -234,8 +205,6 @@ class Filmstrip extends React.PureComponent
myId,
advancedMode,
spotlights,
toolbarsVisible,
permanentTopBar,
classes
} = this.props;
@ -254,14 +223,7 @@ class Filmstrip extends React.PureComponent
};
return (
<div
className={classnames(
classes.root,
toolbarsVisible || permanentTopBar ?
classes.showingToolBar : classes.hiddenToolBar
)}
ref={this.rootContainer}
>
<div className={classes.root}>
<div className={classes.speaker} ref={this.activePeerContainer}>
{ peers[activePeerId] &&
<SpeakerPeer
@ -283,7 +245,7 @@ class Filmstrip extends React.PureComponent
<Me
advancedMode={advancedMode}
style={peerStyle}
smallContainer
smallButtons
/>
</div>
</Grid>
@ -306,7 +268,7 @@ class Filmstrip extends React.PureComponent
advancedMode={advancedMode}
id={peerId}
style={peerStyle}
smallContainer
smallButtons
/>
</div>
</Grid>
@ -334,9 +296,6 @@ Filmstrip.propTypes = {
selectedPeerId : PropTypes.string,
spotlights : PropTypes.array.isRequired,
boxes : PropTypes.number,
toolbarsVisible : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired,
permanentTopBar : PropTypes.bool,
classes : PropTypes.object.isRequired
};
@ -349,10 +308,7 @@ const mapStateToProps = (state) =>
consumers : state.consumers,
myId : state.me.id,
spotlights : state.room.spotlights,
boxes : videoBoxesSelector(state),
toolbarsVisible : state.room.toolbarsVisible,
toolAreaOpen : state.toolarea.toolAreaOpen,
permanentTopBar : state.settings.permanentTopBar
boxes : videoBoxesSelector(state)
};
};
@ -366,9 +322,6 @@ export default withRoomContext(connect(
return (
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
prev.room.selectedPeerId === next.room.selectedPeerId &&
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen &&
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
prev.peers === next.peers &&
prev.consumers === next.consumers &&
prev.room.spotlights === next.room.spotlights &&

View File

@ -4,8 +4,6 @@ import PropTypes from 'prop-types';
import { withSnackbar } from 'notistack';
import * as notificationActions from '../../actions/notificationActions';
const notificationPosition = window.config.notificationPosition || 'right';
class Notifications extends Component
{
displayed = [];
@ -47,7 +45,7 @@ class Notifications extends Component
autoHideDuration : notification.timeout,
anchorOrigin : {
vertical : 'bottom',
horizontal : notificationPosition
horizontal : 'left'
}
}
);

View File

@ -1,14 +1,13 @@
import React from 'react';
import { connect } from 'react-redux';
import { passiveMicConsumerSelector } from '../Selectors';
import { micConsumerSelector } from '../Selectors';
import PropTypes from 'prop-types';
import PeerAudio from './PeerAudio';
const AudioPeers = (props) =>
{
const {
micConsumers,
audioOutputDevice
micConsumers
} = props;
return (
@ -20,7 +19,6 @@ const AudioPeers = (props) =>
<PeerAudio
key={micConsumer.id}
audioTrack={micConsumer.track}
audioOutputDevice={audioOutputDevice}
/>
);
})
@ -31,14 +29,12 @@ const AudioPeers = (props) =>
AudioPeers.propTypes =
{
micConsumers : PropTypes.array,
audioOutputDevice : PropTypes.string
micConsumers : PropTypes.array
};
const mapStateToProps = (state) =>
({
micConsumers : passiveMicConsumerSelector(state),
audioOutputDevice : state.settings.selectedAudioOutputDevice
micConsumers : micConsumerSelector(state)
});
const AudioPeersContainer = connect(
@ -49,10 +45,7 @@ const AudioPeersContainer = connect(
areStatesEqual : (next, prev) =>
{
return (
prev.consumers === next.consumers &&
prev.room.spotlights === next.room.spotlights &&
prev.settings.selectedAudioOutputDevice ===
next.settings.selectedAudioOutputDevice
prev.consumers === next.consumers
);
}
}

View File

@ -10,7 +10,6 @@ export default class PeerAudio extends React.PureComponent
// Latest received audio track.
// @type {MediaStreamTrack}
this._audioTrack = null;
this._audioOutputDevice = null;
}
render()
@ -25,21 +24,17 @@ export default class PeerAudio extends React.PureComponent
componentDidMount()
{
const { audioTrack, audioOutputDevice } = this.props;
const { audioTrack } = this.props;
this._setTrack(audioTrack);
this._setOutputDevice(audioOutputDevice);
}
componentDidUpdate(prevProps)
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(nextProps)
{
if (prevProps !== this.props)
{
const { audioTrack, audioOutputDevice } = this.props;
const { audioTrack } = nextProps;
this._setTrack(audioTrack);
this._setOutputDevice(audioOutputDevice);
}
}
_setTrack(audioTrack)
@ -65,23 +60,9 @@ export default class PeerAudio extends React.PureComponent
audio.srcObject = null;
}
}
_setOutputDevice(audioOutputDevice)
{
if (this._audioOutputDevice === audioOutputDevice)
return;
this._audioOutputDevice = audioOutputDevice;
const { audio } = this.refs;
if (audioOutputDevice && typeof audio.setSinkId === 'function')
audio.setSinkId(audioOutputDevice);
}
}
PeerAudio.propTypes =
{
audioTrack : PropTypes.any,
audioOutputDevice : PropTypes.string
audioTrack : PropTypes.any
};

View File

@ -12,7 +12,6 @@ import { FormattedMessage } from 'react-intl';
import CookieConsent from 'react-cookie-consent';
import CssBaseline from '@material-ui/core/CssBaseline';
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
import Drawer from '@material-ui/core/Drawer';
import Hidden from '@material-ui/core/Hidden';
import Notifications from './Notifications/Notifications';
import MeetingDrawer from './MeetingDrawer/MeetingDrawer';
@ -24,13 +23,8 @@ import VideoWindow from './VideoWindow/VideoWindow';
import LockDialog from './AccessControl/LockDialog/LockDialog';
import Settings from './Settings/Settings';
import TopBar from './Controls/TopBar';
import WakeLock from 'react-wakelock-react16';
import ExtraVideo from './Controls/ExtraVideo';
import ButtonControlBar from './Controls/ButtonControlBar';
import Help from './Controls/Help';
import About from './Controls/About';
const TIMEOUT = window.config.hideTimeout || 5000;
const TIMEOUT = 5 * 1000;
const styles = (theme) =>
({
@ -46,27 +40,6 @@ const styles = (theme) =>
backgroundSize : 'cover',
backgroundRepeat : 'no-repeat'
},
drawer :
{
width : '30vw',
flexShrink : 0,
[theme.breakpoints.down('lg')] :
{
width : '40vw'
},
[theme.breakpoints.down('md')] :
{
width : '50vw'
},
[theme.breakpoints.down('sm')] :
{
width : '70vw'
},
[theme.breakpoints.down('xs')] :
{
width : '90vw'
}
},
drawerPaper :
{
width : '30vw',
@ -165,11 +138,7 @@ class Room extends React.PureComponent
{
const {
room,
browser,
advancedMode,
showNotifications,
buttonControlBar,
drawerOverlayed,
toolAreaOpen,
toggleToolArea,
classes,
@ -182,8 +151,6 @@ class Room extends React.PureComponent
democratic : Democratic
}[room.mode];
const container = window !== undefined ? window.document.body : undefined;
return (
<div className={classes.root}>
{ !isElectron() && false &&
@ -208,9 +175,7 @@ class Room extends React.PureComponent
<AudioPeers />
{ showNotifications &&
<Notifications />
}
<CssBaseline />
@ -220,11 +185,9 @@ class Room extends React.PureComponent
onFullscreen={this.handleToggleFullscreen}
/>
{ (browser.platform === 'mobile' || drawerOverlayed) ?
<nav>
<Hidden implementation='css'>
<SwipeableDrawer
container={container}
variant='temporary'
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={toolAreaOpen}
@ -233,42 +196,14 @@ class Room extends React.PureComponent
classes={{
paper : classes.drawerPaper
}}
ModalProps={{
keepMounted : true // Better open performance on mobile.
}}
>
<MeetingDrawer closeDrawer={toggleToolArea} />
</SwipeableDrawer>
</Hidden>
</nav>
:
<nav className={toolAreaOpen ? classes.drawer : null}>
<Hidden implementation='css'>
<Drawer
variant='persistent'
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={toolAreaOpen}
onClose={() => toggleToolArea()}
classes={{
paper : classes.drawerPaper
}}
>
<MeetingDrawer closeDrawer={toggleToolArea} />
</Drawer>
</Hidden>
</nav>
}
{ browser.platform === 'mobile' && browser.os !== 'ios' &&
<WakeLock />
}
<View advancedMode={advancedMode} />
{ buttonControlBar &&
<ButtonControlBar />
}
{ room.lockDialogOpen &&
<LockDialog />
}
@ -276,17 +211,6 @@ class Room extends React.PureComponent
{ room.settingsOpen &&
<Settings />
}
{ room.extraVideoOpen &&
<ExtraVideo />
}
{ room.helpOpen &&
<Help />
}
{ room.aboutOpen &&
<About />
}
</div>
);
}
@ -295,11 +219,7 @@ class Room extends React.PureComponent
Room.propTypes =
{
room : appPropTypes.Room.isRequired,
browser : PropTypes.object.isRequired,
advancedMode : PropTypes.bool.isRequired,
showNotifications : PropTypes.bool.isRequired,
buttonControlBar : PropTypes.bool.isRequired,
drawerOverlayed : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired,
setToolbarsVisible : PropTypes.func.isRequired,
toggleToolArea : PropTypes.func.isRequired,
@ -310,11 +230,7 @@ Room.propTypes =
const mapStateToProps = (state) =>
({
room : state.room,
browser : state.me.browser,
advancedMode : state.settings.advancedMode,
showNotifications : state.settings.showNotifications,
buttonControlBar : state.settings.buttonControlBar,
drawerOverlayed : state.settings.drawerOverlayed,
toolAreaOpen : state.toolarea.toolAreaOpen
});
@ -339,11 +255,7 @@ export default connect(
{
return (
prev.room === next.room &&
prev.me.browser === next.me.browser &&
prev.settings.advancedMode === next.settings.advancedMode &&
prev.settings.showNotifications === next.settings.showNotifications &&
prev.settings.buttonControlBar === next.settings.buttonControlBar &&
prev.settings.drawerOverlayed === next.settings.drawerOverlayed &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
);
}

View File

@ -1,8 +1,5 @@
import { createSelector } from 'reselect';
const meRolesSelect = (state) => state.me.roles;
const roomPermissionsSelect = (state) => state.room.roomPermissions;
const roomAllowWhenRoleMissing = (state) => state.room.allowWhenRoleMissing;
const producersSelect = (state) => state.producers;
const consumersSelect = (state) => state.consumers;
const spotlightsSelector = (state) => state.room.spotlights;
@ -16,11 +13,6 @@ const peersKeySelector = createSelector(
(peers) => Object.keys(peers)
);
export const peersValueSelector = createSelector(
peersSelector,
(peers) => Object.values(peers)
);
export const lobbyPeersKeySelector = createSelector(
lobbyPeersSelector,
(lobbyPeers) => Object.keys(lobbyPeers)
@ -41,11 +33,6 @@ export const screenProducersSelector = createSelector(
(producers) => Object.values(producers).filter((producer) => producer.source === 'screen')
);
export const extraVideoProducersSelector = createSelector(
producersSelect,
(producers) => Object.values(producers).filter((producer) => producer.source === 'extravideo')
);
export const micProducerSelector = createSelector(
producersSelect,
(producers) => Object.values(producers).find((producer) => producer.source === 'mic')
@ -76,33 +63,6 @@ export const screenConsumerSelector = createSelector(
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen')
);
export const spotlightScreenConsumerSelector = createSelector(
spotlightsSelector,
consumersSelect,
(spotlights, consumers) =>
Object.values(consumers).filter(
(consumer) => consumer.source === 'screen' && spotlights.includes(consumer.peerId)
)
);
export const spotlightExtraVideoConsumerSelector = createSelector(
spotlightsSelector,
consumersSelect,
(spotlights, consumers) =>
Object.values(consumers).filter(
(consumer) => consumer.source === 'extravideo' && spotlights.includes(consumer.peerId)
)
);
export const passiveMicConsumerSelector = createSelector(
spotlightsSelector,
consumersSelect,
(spotlights, consumers) =>
Object.values(consumers).filter(
(consumer) => consumer.source === 'mic' && !spotlights.includes(consumer.peerId)
)
);
export const spotlightsLengthSelector = createSelector(
spotlightsSelector,
(spotlights) => spotlights.length
@ -114,83 +74,35 @@ export const spotlightPeersSelector = createSelector(
(spotlights, peers) => peers.filter((peerId) => spotlights.includes(peerId))
);
export const spotlightSortedPeersSelector = createSelector(
spotlightsSelector,
peersValueSelector,
(spotlights, peers) =>
peers.filter((peer) => spotlights.includes(peer.id) && !peer.raisedHand)
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
);
const raisedHandSortedPeers = createSelector(
peersValueSelector,
(peers) => peers.filter((peer) => peer.raisedHand)
.sort((a, b) => a.raisedHandTimestamp - b.raisedHandTimestamp)
);
const peersSortedSelector = createSelector(
spotlightsSelector,
peersValueSelector,
(spotlights, peers) =>
peers.filter((peer) => !spotlights.includes(peer.id) && !peer.raisedHand)
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
);
export const participantListSelector = createSelector(
raisedHandSortedPeers,
spotlightSortedPeersSelector,
peersSortedSelector,
(raisedHands, spotlights, peers) =>
[ ...raisedHands, ...spotlights, ...peers ]
);
export const peersLengthSelector = createSelector(
peersSelector,
(peers) => Object.values(peers).length
);
export const passivePeersSelector = createSelector(
peersValueSelector,
peersKeySelector,
spotlightsSelector,
(peers, spotlights) => peers.filter((peer) => !spotlights.includes(peer.id))
.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)
(peers, spotlights) => peers.filter((peerId) => !spotlights.includes(peerId))
);
export const videoBoxesSelector = createSelector(
spotlightsLengthSelector,
screenProducersSelector,
spotlightScreenConsumerSelector,
extraVideoProducersSelector,
spotlightExtraVideoConsumerSelector,
(
spotlightsLength,
screenProducers,
screenConsumers,
extraVideoProducers,
extraVideoConsumers
) =>
spotlightsLength + 1 + screenProducers.length +
screenConsumers.length + extraVideoProducers.length +
extraVideoConsumers.length
screenConsumerSelector,
(spotlightsLength, screenProducers, screenConsumers) =>
spotlightsLength + 1 + screenProducers.length + screenConsumers.length
);
export const meProducersSelector = createSelector(
micProducerSelector,
webcamProducerSelector,
screenProducerSelector,
extraVideoProducersSelector,
(micProducer, webcamProducer, screenProducer, extraVideoProducers) =>
(micProducer, webcamProducer, screenProducer) =>
{
return {
micProducer,
webcamProducer,
screenProducer,
extraVideoProducers
screenProducer
};
}
);
@ -213,60 +125,8 @@ export const makePeerConsumerSelector = () =>
consumersArray.find((consumer) => consumer.source === 'webcam');
const screenConsumer =
consumersArray.find((consumer) => consumer.source === 'screen');
const extraVideoConsumers =
consumersArray.filter((consumer) => consumer.source === 'extravideo');
return { micConsumer, webcamConsumer, screenConsumer, extraVideoConsumers };
}
);
};
// Very important that the Components that use this
// selector need to check at least these state changes:
//
// areStatesEqual : (next, prev) =>
// {
// return (
// prev.room.roomPermissions === next.room.roomPermissions &&
// prev.room.allowWhenRoleMissing === next.room.allowWhenRoleMissing &&
// prev.peers === next.peers &&
// prev.me.roles === next.me.roles
// );
// }
export const makePermissionSelector = (permission) =>
{
return createSelector(
meRolesSelect,
roomPermissionsSelect,
roomAllowWhenRoleMissing,
peersValueSelector,
(roles, roomPermissions, allowWhenRoleMissing, peers) =>
{
if (!roomPermissions)
return false;
const permitted = roles.some((role) =>
roomPermissions[permission].includes(role)
);
if (permitted)
return true;
if (!allowWhenRoleMissing)
return false;
// Allow if config is set, and no one is present
if (allowWhenRoleMissing.includes(permission) &&
peers.filter(
(peer) =>
peer.roles.some(
(role) => roomPermissions[permission].includes(role)
)
).length === 0
)
return true;
return false;
return { micConsumer, webcamConsumer, screenConsumer };
}
);
};

View File

@ -1,135 +0,0 @@
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 classnames from 'classnames';
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 Switch from '@material-ui/core/Switch';
const styles = (theme) =>
({
setting :
{
padding : theme.spacing(2)
},
formControl :
{
display : 'flex'
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1),
marginRight : 0
}
});
const AdvancedSettings = ({
roomClient,
settings,
onToggleAdvancedMode,
onToggleNotificationSounds,
classes
}) =>
{
const intl = useIntl();
return (
<React.Fragment>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.advancedMode',
defaultMessage : 'Advanced mode'
})}
/>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.notificationSounds} onChange={onToggleNotificationSounds} value='notificationSounds' />}
labelPlacement='start'
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)));

View File

@ -1,196 +0,0 @@
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 classnames from 'classnames';
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 Switch from '@material-ui/core/Switch';
const styles = (theme) =>
({
setting :
{
padding : theme.spacing(2)
},
formControl :
{
display : 'flex'
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1),
marginRight : 0
}
});
const AppearenceSettings = ({
isMobile,
room,
settings,
onTogglePermanentTopBar,
onToggleHiddenControls,
onToggleButtonControlBar,
onToggleShowNotifications,
onToggleDrawerOverlayed,
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={classnames(classes.setting, classes.switchLabel)}
control={
<Switch checked={settings.permanentTopBar} onChange={onTogglePermanentTopBar} value='permanentTopBar' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.permanentTopBar',
defaultMessage : 'Permanent top bar'
})}
/>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.hiddenControls} onChange={onToggleHiddenControls} value='hiddenControls' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.hiddenControls',
defaultMessage : 'Hidden media controls'
})}
/>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.buttonControlBar} onChange={onToggleButtonControlBar} value='buttonControlBar' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.buttonControlBar',
defaultMessage : 'Separate media controls'
})}
/>
{ !isMobile &&
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.drawerOverlayed} onChange={onToggleDrawerOverlayed} value='drawerOverlayed' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.drawerOverlayed',
defaultMessage : 'Side drawer over content'
})}
/>
}
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.showNotifications} onChange={onToggleShowNotifications} value='showNotifications' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.showNotifications',
defaultMessage : 'Show notifications'
})}
/>
</React.Fragment>
);
};
AppearenceSettings.propTypes =
{
isMobile : PropTypes.bool.isRequired,
room : appPropTypes.Room.isRequired,
settings : PropTypes.object.isRequired,
onTogglePermanentTopBar : PropTypes.func.isRequired,
onToggleHiddenControls : PropTypes.func.isRequired,
onToggleButtonControlBar : PropTypes.func.isRequired,
onToggleShowNotifications : PropTypes.func.isRequired,
onToggleDrawerOverlayed : PropTypes.func.isRequired,
handleChangeMode : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
isMobile : state.me.browser.platform === 'mobile',
room : state.room,
settings : state.settings
});
const mapDispatchToProps = {
onTogglePermanentTopBar : settingsActions.togglePermanentTopBar,
onToggleHiddenControls : settingsActions.toggleHiddenControls,
onToggleShowNotifications : settingsActions.toggleShowNotifications,
onToggleButtonControlBar : settingsActions.toggleButtonControlBar,
onToggleDrawerOverlayed : settingsActions.toggleDrawerOverlayed,
handleChangeMode : roomActions.setDisplayMode
};
export default connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.me.browser === next.me.browser &&
prev.room === next.room &&
prev.settings === next.settings
);
}
}
)(withStyles(styles)(AppearenceSettings));

View File

@ -1,578 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import * as appPropTypes from '../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../RoomContext';
import * as settingsActions from '../../actions/settingsActions';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import classnames from 'classnames';
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 Slider from '@material-ui/core/Slider';
import Typography from '@material-ui/core/Typography';
import Collapse from '@material-ui/core/Collapse';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import Switch from '@material-ui/core/Switch';
const NoiseSlider = withStyles(
{
root :
{
color : '#3880ff',
height : 2,
padding : '15px 0'
},
track : {
height : 2
},
rail : {
height : 2,
opacity : 0.2
},
mark : {
backgroundColor : '#bfbfbf',
height : 10,
width : 3,
marginTop : -3
},
markActive : {
opacity : 1,
backgroundColor : 'currentColor'
}
})(Slider);
const styles = (theme) => ({
setting :
{
padding : theme.spacing(2)
},
margin :
{
height : theme.spacing(3)
},
root : {
width : '100%',
backgroundColor : theme.palette.background.paper
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1)
},
nested : {
display : 'block',
paddingTop : 0,
paddingBottom : 0,
paddingLeft : '25px',
paddingRight : '25px'
},
formControl :
{
display : 'flex'
}
});
const MediaSettings = ({
setEchoCancellation,
setAutoGainControl,
setNoiseSuppression,
setVoiceActivatedUnmute,
roomClient,
me,
volume,
settings,
classes
}) =>
{
const intl = useIntl();
const [ audioSettingsOpen, setAudioSettingsOpen ] = React.useState(false);
const [ videoSettingsOpen, setVideoSettingsOpen ] = React.useState(false);
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.updateWebcam({
restart : true,
newDeviceId : 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>
<List className={classes.root} component='nav'>
<ListItem button onClick={() => setVideoSettingsOpen(!videoSettingsOpen)}>
<ListItemText primary={intl.formatMessage({
id : 'settings.showAdvancedVideo',
defaultMessage : 'Advanced video settings'
})}
/>
{videoSettingsOpen ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={videoSettingsOpen} timeout='auto'>
<FormControl className={classes.formControl}>
<Select
value={settings.resolution || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.updateWebcam({ newResolution: 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>
{ /*
<FormControl className={classes.formControl}>
<Select
value={settings.frameRate || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.updateWebcam({ newFrameRate: event.target.value });
}}
name='Frame rate'
autoWidth
className={classes.selectEmpty}
>
{ [ 1, 5, 10, 15, 20, 25, 30 ].map((frameRate) =>
{
return (
<MenuItem key={frameRate} value={frameRate}>
{frameRate}
</MenuItem>
);
})}
</Select>
<FormHelperText>
<FormattedMessage
id='settings.frameRate'
defaultMessage='Select your video frame rate'
/>
</FormHelperText>
</FormControl>
<FormControl className={classes.formControl}>
<Select
value={settings.screenSharingResolution || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.updateScreenSharing({ newResolution: event.target.value });
}}
name='Screen sharing resolution'
autoWidth
className={classes.selectEmpty}
>
{resolutions.map((resolution, index) =>
{
return (
<MenuItem key={index} value={resolution.value}>
{resolution.label}
</MenuItem>
);
})}
</Select>
<FormHelperText>
<FormattedMessage
id='settings.screenSharingResolution'
defaultMessage='Select your screen sharing resolution'
/>
</FormHelperText>
</FormControl>
*/ }
<FormControl className={classes.formControl}>
<Select
value={settings.screenSharingFrameRate || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.updateScreenSharing({ newFrameRate: event.target.value });
}}
name='Frame rate'
autoWidth
className={classes.selectEmpty}
>
{ [ 1, 5, 10, 15, 20, 25, 30 ].map((screenSharingFrameRate) =>
{
return (
<MenuItem key={screenSharingFrameRate} value={screenSharingFrameRate}>
{screenSharingFrameRate}
</MenuItem>
);
})}
</Select>
<FormHelperText>
<FormattedMessage
id='settings.screenSharingFrameRate'
defaultMessage='Select your screen sharing frame rate'
/>
</FormHelperText>
</FormControl>
</Collapse>
</List>
</form>
<form className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
<Select
value={settings.selectedAudioDevice || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.updateMic({ restart: true, newDeviceId: event.target.value });
}}
displayEmpty
name={intl.formatMessage({
id : 'settings.audio',
defaultMessage : 'Audio input device'
})}
autoWidth
className={classes.selectEmpty}
disabled={audioDevices.length === 0 || me.audioInProgress}
>
{ audioDevices.map((audio, index) =>
{
return (
<MenuItem key={index} value={audio.deviceId}>{audio.label==='' ? index+1 : audio.label }</MenuItem>
);
})}
</Select>
<FormHelperText>
{ audioDevices.length > 0 ?
intl.formatMessage({
id : 'settings.selectAudio',
defaultMessage : 'Select audio input device'
})
:
intl.formatMessage({
id : 'settings.cantSelectAudio',
defaultMessage : 'Unable to select audio input device'
})
}
</FormHelperText>
</FormControl>
{ 'audioOutputSupportedBrowsers' in window.config &&
window.config.audioOutputSupportedBrowsers.includes(me.browser.name) &&
<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>
}
<List className={classes.root} component='nav'>
<ListItem button onClick={() => setAudioSettingsOpen(!audioSettingsOpen)}>
<ListItemText primary={intl.formatMessage({
id : 'settings.showAdvancedAudio',
defaultMessage : 'Advanced audio settings'
})}
/>
{audioSettingsOpen ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={audioSettingsOpen} timeout='auto'>
<List component='div'>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.echoCancellation}
onChange={
(event) =>
{
setEchoCancellation(event.target.checked);
roomClient.updateMic();
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.echoCancellation',
defaultMessage : 'Echo cancellation'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.autoGainControl} onChange={
(event) =>
{
setAutoGainControl(event.target.checked);
roomClient.updateMic();
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.autoGainControl',
defaultMessage : 'Auto gain control'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.noiseSuppression} onChange={
(event) =>
{
setNoiseSuppression(event.target.checked);
roomClient.updateMic();
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.noiseSuppression',
defaultMessage : 'Noise suppression'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.voiceActivatedUnmute} onChange={
(event) =>
{
setVoiceActivatedUnmute(event.target.checked);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.voiceActivatedUnmute',
defaultMessage : 'Voice activated unmute'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<div className={classes.margin} />
<Typography gutterBottom>
{
intl.formatMessage({
id : 'settings.noiseThreshold',
defaultMessage : 'Noise threshold'
})
}:
</Typography>
<NoiseSlider className={classnames(classes.slider, classnames.setting)}
key={'noise-threshold-slider'}
min={-100}
value={settings.noiseThreshold}
max={0}
valueLabelDisplay={'auto'}
onChange={
(event, value) =>
{
roomClient._setNoiseThreshold(value);
}}
marks={[ { value: volume, label: `${volume} dB` } ]}
/>
</ListItem>
</List>
</Collapse>
</List>
</form>
</React.Fragment>
);
};
MediaSettings.propTypes =
{
roomClient : PropTypes.any.isRequired,
setEchoCancellation : PropTypes.func.isRequired,
setAutoGainControl : PropTypes.func.isRequired,
setNoiseSuppression : PropTypes.func.isRequired,
setVoiceActivatedUnmute : PropTypes.func.isRequired,
me : appPropTypes.Me.isRequired,
volume : PropTypes.number,
settings : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
{
return {
me : state.me,
volume : state.peerVolumes[state.me.id],
settings : state.settings
};
};
const mapDispatchToProps = {
setEchoCancellation : settingsActions.setEchoCancellation,
setAutoGainControl : settingsActions.setAutoGainControl,
setNoiseSuppression : settingsActions.setNoiseSuppression,
setVoiceActivatedUnmute : settingsActions.setVoiceActivatedUnmute
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.me === next.me &&
prev.settings === next.settings &&
prev.peerVolumes[prev.me.id] === next[next.me.id]
);
}
}
)(withStyles(styles)(MediaSettings)));

View File

@ -1,25 +1,22 @@
import React from 'react';
import { connect } from 'react-redux';
import * as appPropTypes from '../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../RoomContext';
import * as roomActions from '../../actions/roomActions';
import * as settingsActions from '../../actions/settingsActions';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import MediaSettings from './MediaSettings';
import AppearenceSettings from './AppearenceSettings';
import AdvancedSettings from './AdvancedSettings';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
const tabs =
[
'media',
'appearence',
'advanced'
];
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) =>
({
@ -46,27 +43,99 @@ const styles = (theme) =>
width : '90vw'
}
},
tabsHeader :
setting :
{
flexGrow : 1
padding : theme.spacing(2)
},
formControl :
{
display : 'flex'
}
});
const Settings = ({
currentSettingsTab,
settingsOpen,
roomClient,
room,
me,
settings,
onToggleAdvancedMode,
onTogglePermanentTopBar,
handleCloseSettings,
setSettingsTab,
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'
})
} ];
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 = [];
return (
<Dialog
className={classes.root}
open={settingsOpen}
onClose={() => handleCloseSettings(false)}
open={room.settingsOpen}
onClose={() => handleCloseSettings({ settingsOpen: false })}
classes={{
paper : classes.dialogPaper
}}
@ -77,40 +146,201 @@ const Settings = ({
defaultMessage='Settings'
/>
</DialogTitle>
<Tabs
className={classes.tabsHeader}
value={tabs.indexOf(currentSettingsTab)}
onChange={(event, value) => setSettingsTab(tabs[value])}
indicatorColor='primary'
textColor='primary'
variant='fullWidth'
<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}
>
<Tab
label={
{ webcams.map((webcam, index) =>
{
return (
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
);
})}
</Select>
<FormHelperText>
{ webcams.length > 0 ?
intl.formatMessage({
id : 'label.media',
defaultMessage : 'Media'
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>
<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'
/>
<Tab
</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 : 'label.appearance',
defaultMessage : 'Appearence'
id : 'settings.advancedMode',
defaultMessage : 'Advanced mode'
})}
/>
<Tab
{ settings.advancedMode &&
<React.Fragment>
<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}
>
{ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ].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}
control={<Checkbox checked={settings.permanentTopBar} onChange={onTogglePermanentTopBar} value='permanentTopBar' />}
label={intl.formatMessage({
id : 'label.advanced',
defaultMessage : 'Advanced'
id : 'settings.permanentTopBar',
defaultMessage : 'Permanent top bar'
})}
/>
</Tabs>
{currentSettingsTab === 'media' && <MediaSettings />}
{currentSettingsTab === 'appearence' && <AppearenceSettings />}
{currentSettingsTab === 'advanced' && <AdvancedSettings />}
</React.Fragment>
}
<DialogActions>
<Button onClick={() => handleCloseSettings(false)} color='primary'>
<Button onClick={() => handleCloseSettings({ settingsOpen: false })} color='primary'>
<FormattedMessage
id='label.close'
defaultMessage='Close'
@ -123,25 +353,34 @@ const Settings = ({
Settings.propTypes =
{
currentSettingsTab : PropTypes.string.isRequired,
settingsOpen : PropTypes.bool.isRequired,
roomClient : PropTypes.any.isRequired,
me : appPropTypes.Me.isRequired,
room : appPropTypes.Room.isRequired,
settings : PropTypes.object.isRequired,
onToggleAdvancedMode : PropTypes.func.isRequired,
onTogglePermanentTopBar : PropTypes.func.isRequired,
handleChangeMode : PropTypes.func.isRequired,
handleCloseSettings : PropTypes.func.isRequired,
setSettingsTab : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
currentSettingsTab : state.room.currentSettingsTab,
settingsOpen : state.room.settingsOpen
});
const mapDispatchToProps = {
handleCloseSettings : roomActions.setSettingsOpen,
setSettingsTab : roomActions.setSettingsTab
{
return {
me : state.me,
room : state.room,
settings : state.settings
};
};
export default connect(
const mapDispatchToProps = {
onToggleAdvancedMode : settingsActions.toggleAdvancedMode,
onTogglePermanentTopBar : settingsActions.togglePermanentTopBar,
handleChangeMode : roomActions.setDisplayMode,
handleCloseSettings : roomActions.setSettingsOpen
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
@ -149,9 +388,10 @@ export default connect(
areStatesEqual : (next, prev) =>
{
return (
prev.room.currentSettingsTab === next.room.currentSettingsTab &&
prev.room.settingsOpen === next.room.settingsOpen
prev.me === next.me &&
prev.room === next.room &&
prev.settings === next.settings
);
}
}
)(withStyles(styles)(Settings));
)(withStyles(styles)(Settings)));

View File

@ -1,149 +0,0 @@
import React from 'react';
import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import Grid from '@material-ui/core/Grid';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
import WebAssetIcon from '@material-ui/icons/WebAsset';
import ErrorIcon from '@material-ui/icons/Error';
import Hidden from '@material-ui/core/Hidden';
const styles = (theme) =>
({
dialogPaper :
{
width : '40vw',
[theme.breakpoints.down('lg')] :
{
width : '40vw'
},
[theme.breakpoints.down('md')] :
{
width : '50vw'
},
[theme.breakpoints.down('sm')] :
{
width : '70vw'
},
[theme.breakpoints.down('xs')] :
{
width : '90vw'
}
// display : 'flex',
// flexDirection : 'column'
},
list : {
backgroundColor : theme.palette.background.paper
},
errorAvatar : {
width : theme.spacing(20),
height : theme.spacing(20)
}
});
let dense = false;
const supportedBrowsers=[
{ name: 'Chrome/Chromium', version: '74', vendor: 'Google' },
{ name: 'Edge', version: '18', vendor: 'Microsoft' },
{ name: 'Firefox', version: '60', vendor: 'Mozilla' },
{ name: 'Safari', version: '12', vendor: 'Apple' },
{ name: 'Opera', version: '62', vendor: '' },
// { name: 'Brave', version: '1.5', vendor: '' },
// { name: 'Vivaldi', version: '3', vendor: '' },
{ name: 'Samsung Internet', version: '11.1.1.52', vendor: '' }
];
const UnsupportedBrowser = ({
platform,
webrtcUnavailable,
classes
}) =>
{
if (platform !== 'desktop')
dense = true;
return (
<Dialog
open
scroll={'body'}
classes={{
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>
{!webrtcUnavailable &&
<FormattedMessage
id='unsupportedBrowser.titleUnsupportedBrowser'
defaultMessage='Browser not supported'
/>
}
{webrtcUnavailable &&
<FormattedMessage
id='unsupportedBrowser.titlewebrtcUnavailable'
defaultMessage='Required functionality not availble in your browser'
/>
}
</DialogTitle>
<DialogContent dividers>
<FormattedMessage
id='unsupportedBrowser.bodyText'
defaultMessage='This meeting service requires
functionality not supported by your browser.
Please upgrade, switch to a different browser, or
check your settings. Supported browsers:'
/>
<Grid container spacing={2} justify='center' alignItems='center'>
<Grid item xs={12} md={7}>
<div className={classes.list}>
<List dense={dense}>
{supportedBrowsers.map((browser, index) =>
{
const supportedBrowser = `${browser.vendor} ${browser.name}`;
const supportedVersion = `${browser.version}+`;
return (
<ListItem key={index}>
<ListItemAvatar>
<Avatar>
<WebAssetIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={supportedBrowser}
secondary={supportedVersion}
/>
</ListItem>
);
})}
</List>
</div>
</Grid>
<Grid item xs={12} md={5} align='center'>
<Hidden mdDown>
<ErrorIcon className={classes.errorAvatar} color='error'/>
</Hidden>
</Grid>
</Grid>
</DialogContent>
</Dialog>
);
};
UnsupportedBrowser.propTypes =
{
webrtcUnavailable : PropTypes.bool.isRequired,
platform : PropTypes.string.isRequired,
classes : PropTypes.object.isRequired
};
export default withStyles(styles)(UnsupportedBrowser);

View File

@ -96,6 +96,11 @@ const FullScreenView = (props) =>
!consumer.remotelyPaused
);
let consumerProfile;
if (consumer)
consumerProfile = consumer.profile;
return (
<div className={classes.root}>
<div className={classes.controls}>
@ -116,25 +121,9 @@ const FullScreenView = (props) =>
<VideoView
advancedMode={advancedMode}
videoContain
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}
videoTrack={consumer ? consumer.track : null}
videoVisible={consumerVisible}
videoCodec={consumer && consumer.codec}
videoScore={consumer ? consumer.score : null}
videoProfile={consumerProfile}
/>
</div>
);

View File

@ -81,15 +81,12 @@ class FullView extends React.PureComponent
this._setTracks(videoTrack);
}
componentDidUpdate(prevProps)
{
if (prevProps !== this.props)
componentDidUpdate()
{
const { videoTrack } = this.props;
this._setTracks(videoTrack);
}
}
_setTracks(videoTrack)
{

View File

@ -3,15 +3,6 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import EditableInput from '../Controls/EditableInput';
import Logger from '../../Logger';
import { yellow, orange, red } from '@material-ui/core/colors';
import SignalCellularOffIcon from '@material-ui/icons/SignalCellularOff';
import SignalCellular0BarIcon from '@material-ui/icons/SignalCellular0Bar';
import SignalCellular1BarIcon from '@material-ui/icons/SignalCellular1Bar';
import SignalCellular2BarIcon from '@material-ui/icons/SignalCellular2Bar';
import SignalCellular3BarIcon from '@material-ui/icons/SignalCellular3Bar';
const logger = new Logger('VideoView');
const styles = (theme) =>
({
@ -25,7 +16,6 @@ const styles = (theme) =>
flexDirection : 'column',
overflow : 'hidden'
},
video :
{
flex : '100 100 auto',
@ -70,66 +60,24 @@ const styles = (theme) =>
{
display : 'flex',
transitionProperty : 'opacity',
transitionDuration : '.15s'
transitionDuration : '.15s',
'&.hidden' :
{
opacity : 0,
transitionDuration : '0s'
}
},
box :
{
padding : theme.spacing(0.5),
borderRadius : 2,
backgroundColor : 'rgba(0, 0, 0, 0.25)',
'& p' :
{
userSelect : 'none',
margin : 0,
color : 'rgba(255, 255, 255, 0.7)',
fontSize : '0.8em',
'&.left' :
{
backgroundColor : 'rgba(0, 0, 0, 0.25)',
display : 'grid',
gap : '1px 5px',
// eslint-disable-next-line
gridTemplateAreas : '\
"AcodL Acod Acod Acod Acod" \
"VcodL Vcod Vcod Vcod Vcod" \
"ResL Res Res Res Res" \
"RecvL RecvBps RecvBps RecvSum RecvSum" \
"SendL SendBps SendBps SendSum SendSum" \
"IPlocL IPloc IPloc IPloc IPloc" \
"IPsrvL IPsrv IPsrv IPsrv IPsrv" \
"STLcurrL STLcurr STLcurr STLcurr STLcurr" \
"STLprefL STLpref STLpref STLpref STLpref"',
'& .AcodL' : { gridArea: 'AcodL' },
'& .Acod' : { gridArea: 'Acod' },
'& .VcodL' : { gridArea: 'VcodL' },
'& .Vcod' : { gridArea: 'Vcod' },
'& .ResL' : { gridArea: 'ResL' },
'& .Res' : { gridArea: 'Res' },
'& .RecvL' : { gridArea: 'RecvL' },
'& .RecvBps' : { gridArea: 'RecvBps', justifySelf: 'flex-end' },
'& .RecvSum' : { gridArea: 'RecvSum', justifySelf: 'flex-end' },
'& .SendL' : { gridArea: 'SendL' },
'& .SendBps' : { gridArea: 'SendBps', justifySelf: 'flex-end' },
'& .SendSum' : { gridArea: 'SendSum', justifySelf: 'flex-end' },
'& .IPlocL' : { gridArea: 'IPlocL' },
'& .IPloc' : { gridArea: 'IPloc' },
'& .IPsrvL' : { gridArea: 'IPsrvL' },
'& .IPsrv' : { gridArea: 'IPsrv' },
'& .STLcurrL' : { gridArea: 'STLcurrL' },
'& .STLcurr' : { gridArea: 'STLcurr' },
'& .STLprefL' : { gridArea: 'STLprefL' },
'& .STLpref' : { gridArea: 'STLpref' }
},
'&.right' :
{
marginLeft : 'auto',
width : 30
},
'&.hidden' :
{
opacity : 0,
transitionDuration : '0s'
fontSize : '0.8em'
}
},
peer :
@ -171,10 +119,6 @@ class VideoView extends React.PureComponent
videoHeight : null
};
// Latest received audio track
// @type {MediaStreamTrack}
this._audioTrack = null;
// Latest received video track.
// @type {MediaStreamTrack}
this._videoTrack = null;
@ -188,16 +132,16 @@ class VideoView extends React.PureComponent
const {
isMe,
isScreen,
isExtraVideo,
showQuality,
displayName,
showPeerInfo,
videoContain,
advancedMode,
videoVisible,
videoMultiLayer,
audioScore,
videoScore,
// audioScore,
// videoScore,
// consumerSpatialLayers,
// consumerTemporalLayers,
consumerCurrentSpatialLayer,
consumerCurrentTemporalLayer,
consumerPreferredSpatialLayer,
@ -206,8 +150,7 @@ class VideoView extends React.PureComponent
videoCodec,
onChangeDisplayName,
children,
classes,
netInfo
classes
} = this.props;
const {
@ -215,155 +158,41 @@ class VideoView extends React.PureComponent
videoHeight
} = this.state;
let quality = null;
if (showQuality)
{
quality = <SignalCellularOffIcon style={{ color: red[500] }}/>;
if (videoScore || audioScore)
{
const score = videoScore ? videoScore : audioScore;
switch (isMe ? score.score : score.producerScore)
{
case 0:
case 1:
{
quality = <SignalCellular0BarIcon style={{ color: red[500] }}/>;
break;
}
case 2:
case 3:
{
quality = <SignalCellular1BarIcon style={{ color: red[500] }}/>;
break;
}
case 4:
case 5:
case 6:
{
quality = <SignalCellular2BarIcon style={{ color: orange[500] }}/>;
break;
}
case 7:
case 8:
case 9:
{
quality = <SignalCellular3BarIcon style={{ color: yellow[500] }}/>;
break;
}
case 10:
{
quality = null;
break;
}
default:
{
break;
}
}
}
}
return (
<div className={classes.root}>
<div className={classes.info}>
<div className={classes.media}>
<div className={classnames(classes.box, 'left', { hidden: !advancedMode })}>
{ audioCodec &&
<React.Fragment>
<span className={'AcodL'}>Acod: </span>
<span className={'Acod'}>
{audioCodec}
</span>
</React.Fragment>
}
<div className={classnames(classes.media,
{
hidden : !advancedMode
})}
>
<div className={classes.box}>
{ audioCodec && <p>{audioCodec}</p> }
{ videoCodec &&
<React.Fragment>
<span className={'VcodL'}>Vcod: </span>
<span className={'Vcod'}>
<p>
{videoCodec}
</span>
</React.Fragment>
}
{ (videoVisible && videoWidth !== null) &&
<React.Fragment>
<span className={'ResL'}>Res: </span>
<span className={'Res'}>
{videoWidth}x{videoHeight}
</span>
</React.Fragment>
}
{ isMe && !isScreen && !isExtraVideo &&
(netInfo.recv && netInfo.send && netInfo.send.iceSelectedTuple) &&
<React.Fragment>
<span className={'RecvL'}>Recv: </span>
<span className={'RecvBps'}>
{(netInfo.recv.sendBitrate/1024/1024).toFixed(2)}Mb/s
</span>
<span className={'RecvSum'}>
{(netInfo.recv.bytesSent/1024/1024).toFixed(2)}MB
</span>
<span className={'SendL'}>Send: </span>
<span className={'SendBps'}>
{(netInfo.send.recvBitrate/1024/1024).toFixed(2)}Mb/s
</span>
<span className={'SendSum'}>
{(netInfo.send.bytesReceived/1024/1024).toFixed(2)}MB
</span>
<span className={'IPlocL'}>IPloc: </span>
<span className={'IPloc'}>
{netInfo.send.iceSelectedTuple.remoteIp}
</span>
<span className={'IPsrvL'}>IPsrv: </span>
<span className={'IPsrv'}>
{netInfo.send.iceSelectedTuple.localIp}
</span>
</React.Fragment>
</p>
}
{ videoMultiLayer &&
<React.Fragment>
<span className={'STLcurrL'}>STLcurr: </span>
<span className={'STLcurr'}>{consumerCurrentSpatialLayer} {consumerCurrentTemporalLayer}</span>
<span className={'STLprefL'}>STLpref: </span>
<span className={'STLpref'}>{consumerPreferredSpatialLayer} {consumerPreferredTemporalLayer}</span>
</React.Fragment>
<p>
{`current spatial-temporal layers: ${consumerCurrentSpatialLayer} ${consumerCurrentTemporalLayer}`}
<br />
{`preferred spatial-temporal layers: ${consumerPreferredSpatialLayer} ${consumerPreferredTemporalLayer}`}
</p>
}
</div>
{ showQuality &&
<div className={classnames(classes.box, 'right')}>
{
quality
{ (videoVisible && videoWidth !== null) &&
<p>{videoWidth}x{videoHeight}</p>
}
</div>
}
</div>
{ showPeerInfo &&
<div className={classes.peer}>
<div className={classes.box}>
{ isMe ?
<React.Fragment>
<EditableInput
value={displayName}
propName='newDisplayName'
@ -376,11 +205,8 @@ class VideoView extends React.PureComponent
autoCorrect : 'off',
spellCheck : false
}}
onChange={
({ newDisplayName }) =>
onChangeDisplayName(newDisplayName)}
onChange={({ newDisplayName }) => onChangeDisplayName(newDisplayName)}
/>
</React.Fragment>
:
<span className={classes.displayNameStatic}>
{displayName}
@ -392,7 +218,7 @@ class VideoView extends React.PureComponent
</div>
<video
ref='videoElement'
ref='video'
className={classnames(classes.video, {
hidden : !videoVisible,
'isMe' : isMe && !isScreen,
@ -400,16 +226,6 @@ class VideoView extends React.PureComponent
})}
autoPlay
playsInline
muted
controls={false}
/>
<audio
ref='audioElement'
autoPlay
playsInline
muted={isMe}
controls={false}
/>
{children}
@ -419,87 +235,52 @@ class VideoView extends React.PureComponent
componentDidMount()
{
const { videoTrack, audioTrack } = this.props;
const { videoTrack } = this.props;
this._setTracks(videoTrack, audioTrack);
this._setTracks(videoTrack);
}
componentWillUnmount()
{
clearInterval(this._videoResolutionTimer);
const { videoElement } = this.refs;
if (videoElement)
{
videoElement.oncanplay = null;
videoElement.onplay = null;
videoElement.onpause = null;
}
}
componentDidUpdate(prevProps)
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(nextProps)
{
if (prevProps !== this.props)
{
const { videoTrack, audioTrack } = this.props;
const { videoTrack } = nextProps;
this._setTracks(videoTrack);
this._setTracks(videoTrack, audioTrack);
}
}
_setTracks(videoTrack, audioTrack)
_setTracks(videoTrack)
{
if (this._videoTrack === videoTrack && this._audioTrack === audioTrack)
if (this._videoTrack === videoTrack)
return;
this._videoTrack = videoTrack;
this._audioTrack = audioTrack;
clearInterval(this._videoResolutionTimer);
this._hideVideoResolution();
const { videoElement, audioElement } = this.refs;
const { video } = this.refs;
if (videoTrack)
{
const stream = new MediaStream();
if (videoTrack)
stream.addTrack(videoTrack);
videoElement.srcObject = stream;
videoElement.oncanplay = () => this.setState({ videoCanPlay: true });
videoElement.onplay = () =>
{
audioElement.play()
.catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
};
videoElement.play()
.catch((error) => logger.warn('videoElement.play() [error:"%o]', error));
video.srcObject = stream;
if (videoTrack)
this._showVideoResolution();
}
else
{
videoElement.srcObject = null;
}
if (audioTrack)
{
const stream = new MediaStream();
stream.addTrack(audioTrack);
audioElement.srcObject = stream;
audioElement.play()
.catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
}
else
{
audioElement.srcObject = null;
video.srcObject = null;
}
}
@ -508,19 +289,16 @@ class VideoView extends React.PureComponent
this._videoResolutionTimer = setInterval(() =>
{
const { videoWidth, videoHeight } = this.state;
const { videoElement } = this.refs;
const { video } = this.refs;
// Don't re-render if nothing changed.
if (
videoElement.videoWidth === videoWidth &&
videoElement.videoHeight === videoHeight
)
if (video.videoWidth === videoWidth && video.videoHeight === videoHeight)
return;
this.setState(
{
videoWidth : videoElement.videoWidth,
videoHeight : videoElement.videoHeight
videoWidth : video.videoWidth,
videoHeight : video.videoHeight
});
}, 1000);
}
@ -535,14 +313,11 @@ VideoView.propTypes =
{
isMe : PropTypes.bool,
isScreen : PropTypes.bool,
isExtraVideo : PropTypes.bool,
showQuality : PropTypes.bool,
displayName : PropTypes.string,
showPeerInfo : PropTypes.bool,
videoContain : PropTypes.bool,
advancedMode : PropTypes.bool,
videoTrack : PropTypes.any,
audioTrack : PropTypes.any,
videoVisible : PropTypes.bool.isRequired,
consumerSpatialLayers : PropTypes.number,
consumerTemporalLayers : PropTypes.number,
@ -557,8 +332,7 @@ VideoView.propTypes =
videoCodec : PropTypes.string,
onChangeDisplayName : PropTypes.func,
children : PropTypes.object,
classes : PropTypes.object.isRequired,
netInfo : PropTypes.object
classes : PropTypes.object.isRequired
};
export default withStyles(styles)(VideoView);

View File

@ -23,29 +23,18 @@ const VideoWindow = (props) =>
!consumer.remotelyPaused
);
let consumerProfile;
if (consumer)
consumerProfile = consumer.profile;
return (
<NewWindow onUnload={toggleConsumerWindow}>
<FullView
advancedMode={advancedMode}
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}
videoTrack={consumer ? consumer.track : null}
videoVisible={consumerVisible}
videoCodec={consumer && consumer.codec}
videoScore={consumer ? consumer.score : null}
videoProfile={consumerProfile}
/>
</NewWindow>
);

View File

@ -18,9 +18,9 @@ export const Me = PropTypes.shape(
export const Producer = PropTypes.shape(
{
id : PropTypes.string.isRequired,
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired,
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
deviceLabel : PropTypes.string,
type : PropTypes.oneOf([ 'front', 'back', 'screen', 'extravideo' ]),
type : PropTypes.oneOf([ 'front', 'back', 'screen' ]),
paused : PropTypes.bool.isRequired,
track : PropTypes.any,
codec : PropTypes.string.isRequired
@ -37,7 +37,7 @@ export const Consumer = PropTypes.shape(
{
id : PropTypes.string.isRequired,
peerId : PropTypes.string.isRequired,
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired,
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
locallyPaused : PropTypes.bool.isRequired,
remotelyPaused : PropTypes.bool.isRequired,
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),

View File

@ -24,9 +24,7 @@ export default function()
return {
flag,
os : browser.getOSName(true), // ios, android, linux...
platform : browser.getPlatformType(true), // mobile, desktop, tablet
name : browser.getBrowserName(true),
name : browser.getBrowserName(),
version : browser.getBrowserVersion(),
bowser : browser
};

View File

@ -0,0 +1,26 @@
<?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>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,26 @@
<?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>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -12,7 +12,6 @@ import RoomClient from './RoomClient';
import RoomContext from './RoomContext';
import deviceInfo from './deviceInfo';
import * as meActions from './actions/meActions';
import UnsupportedBrowser from './components/UnsupportedBrowser';
import ChooseRoom from './components/ChooseRoom';
import LoadingView from './components/LoadingView';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
@ -21,7 +20,7 @@ import { persistor, store } from './store';
import { SnackbarProvider } from 'notistack';
import * as serviceWorker from './serviceWorker';
import { ReactLazyPreload } from './components/ReactLazyPreload';
import { detectDevice } from 'mediasoup-client';
// import messagesEnglish from './translations/en';
import messagesNorwegian from './translations/nb';
import messagesGerman from './translations/de';
@ -32,15 +31,13 @@ import messagesFrench from './translations/fr';
import messagesGreek from './translations/el';
import messagesRomanian from './translations/ro';
import messagesPortuguese from './translations/pt';
import messagesChineseSimplified from './translations/cn';
import messagesChineseTraditional from './translations/tw';
import messagesChinese from './translations/cn';
import messagesSpanish from './translations/es';
import messagesCroatian from './translations/hr';
import messagesCzech from './translations/cs';
import messagesItalian from './translations/it';
import messagesUkrainian from './translations/uk';
import messagesTurkish from './translations/tr';
import messagesLatvian from './translations/lv';
import './index.css';
@ -60,48 +57,22 @@ const messages =
'el' : messagesGreek,
'ro' : messagesRomanian,
'pt' : messagesPortuguese,
'zh-hans' : messagesChineseSimplified,
'zh-hant' : messagesChineseTraditional,
'zh' : messagesChinese,
'es' : messagesSpanish,
'hr' : messagesCroatian,
'cs' : messagesCzech,
'it' : messagesItalian,
'uk' : messagesUkrainian,
'tr' : messagesTurkish,
'lv' : messagesLatvian
'tr' : messagesTurkish
};
const supportedBrowsers={
'windows' : {
'internet explorer' : '>12',
'microsoft edge' : '>18'
},
'safari' : '>12',
'firefox' : '>=60',
'chrome' : '>=74',
'opera' : '>=62',
'samsung internet for android' : '>=11.1.1.52'
};
const browserLanguage = (navigator.language || navigator.browserLanguage).toLowerCase();
let locale = browserLanguage.split(/[-_]/)[0]; // language without region code
if (locale === 'zh')
{
if (browserLanguage === 'zh-cn')
locale = 'zh-hans';
else
locale = 'zh-hant';
}
const locale = navigator.language.split(/[-_]/)[0]; // language without region code
const intl = createIntl({
locale,
messages : messages[locale]
}, cache);
document.documentElement.lang = locale;
if (process.env.REACT_APP_DEBUG === '*' || process.env.NODE_ENV !== 'production')
{
debug.enable('* -engine* -socket* -RIE* *WARN* *ERROR*');
@ -139,75 +110,15 @@ function run()
const accessCode = parameters.get('code');
const produce = parameters.get('produce') !== 'false';
const useSimulcast = parameters.get('simulcast') === 'true';
const useSharingSimulcast = parameters.get('sharingSimulcast') === 'true';
const forceTcp = parameters.get('forceTcp') === 'true';
const displayName = parameters.get('displayName');
const muted = parameters.get('muted') === 'true';
const { pathname } = window.location;
let basePath = pathname.substring(0, pathname.lastIndexOf('/'));
if (!basePath)
basePath = '/';
// Get current device.
const device = deviceInfo();
let unsupportedBrowser = false;
let webrtcUnavailable = false;
if (detectDevice() === undefined)
{
logger.error('Your browser is not supported [deviceInfo:"%o"]', device);
unsupportedBrowser = true;
}
else if (
navigator.mediaDevices === undefined ||
navigator.mediaDevices.getUserMedia === undefined ||
window.RTCPeerConnection === undefined
)
{
logger.error('Your browser is not supported [deviceInfo:"%o"]', device);
webrtcUnavailable = true;
}
else if (
!device.bowser.satisfies(
window.config.supportedBrowsers || supportedBrowsers
)
)
{
logger.error(
'Your browser is not supported [deviceInfo:"%o"]',
device
);
unsupportedBrowser = true;
}
else
{
logger.debug('Your browser is supported [deviceInfo:"%o"]', device);
}
if (unsupportedBrowser || webrtcUnavailable)
{
render(
<MuiThemeProvider theme={theme}>
<RawIntlProvider value={intl}>
<UnsupportedBrowser
webrtcUnavailable={webrtcUnavailable}
platform={device.platform}
/>
</RawIntlProvider>
</MuiThemeProvider>,
document.getElementById('multiparty-meeting')
);
return;
}
store.dispatch(
meActions.setMe({
peerId,
@ -220,11 +131,12 @@ function run()
peerId,
accessCode,
device,
useSimulcast,
useSharingSimulcast,
produce,
forceTcp,
displayName,
muted,
basePath
muted
});
global.CLIENT = roomClient;
@ -236,7 +148,7 @@ function run()
<PersistGate loading={<LoadingView />} persistor={persistor}>
<RoomContext.Provider value={roomClient}>
<SnackbarProvider>
<Router basename={basePath}>
<Router>
<Suspense fallback={<LoadingView />}>
<React.Fragment>
<Route exact path='/' component={ChooseRoom} />

View File

@ -1,20 +0,0 @@
export const permissions = {
// The role(s) have permission to lock/unlock a room
CHANGE_ROOM_LOCK : 'CHANGE_ROOM_LOCK',
// The role(s) have permission to promote a peer from the lobby
PROMOTE_PEER : 'PROMOTE_PEER',
// The role(s) have permission to send chat messages
SEND_CHAT : 'SEND_CHAT',
// The role(s) have permission to moderate chat
MODERATE_CHAT : 'MODERATE_CHAT',
// The role(s) have permission to share screen
SHARE_SCREEN : 'SHARE_SCREEN',
// The role(s) have permission to produce extra video
EXTRA_VIDEO : 'EXTRA_VIDEO',
// The role(s) have permission to share files
SHARE_FILE : 'SHARE_FILE',
// The role(s) have permission to moderate files
MODERATE_FILES : 'MODERATE_FILES',
// The role(s) have permission to moderate room (e.g. kick user)
MODERATE_ROOM : 'MODERATE_ROOM'
};

View File

@ -30,11 +30,6 @@ const chat = (state = [], action) =>
return [ ...state, ...chatHistory ];
}
case 'CLEAR_CHAT':
{
return [];
}
default:
return state;
}

View File

@ -110,11 +110,6 @@ const consumers = (state = initialState, action) =>
return { ...state, [consumerId]: newConsumer };
}
case 'CLEAR_CONSUMERS':
{
return initialState;
}
default:
return state;
}

View File

@ -85,9 +85,6 @@ const files = (state = {}, action) =>
return { ...state, [magnetUri]: newFile };
}
case 'CLEAR_FILES':
return {};
default:
return state;
}

View File

@ -2,8 +2,6 @@ const initialState =
{
id : null,
picture : null,
browser : null,
roles : [ 'normal' ], // Default role
canSendMic : false,
canSendWebcam : false,
canShareScreen : false,
@ -15,11 +13,9 @@ const initialState =
screenShareInProgress : false,
displayNameInProgress : false,
loginEnabled : false,
raisedHand : false,
raisedHandInProgress : false,
loggedIn : false,
isSpeaking : false,
isAutoMuted : true
raiseHand : false,
raiseHandInProgress : false,
loggedIn : false
};
const me = (state = initialState, action) =>
@ -40,13 +36,6 @@ const me = (state = initialState, action) =>
};
}
case 'SET_BROWSER':
{
const { browser } = action.payload;
return { ...state, browser };
}
case 'LOGGED_IN':
{
const { flag } = action.payload;
@ -54,24 +43,6 @@ const me = (state = initialState, action) =>
return { ...state, loggedIn: flag };
}
case 'ADD_ROLE':
{
if (state.roles.includes(action.payload.role))
return state;
const roles = [ ...state.roles, action.payload.role ];
return { ...state, roles };
}
case 'REMOVE_ROLE':
{
const roles = state.roles.filter((role) =>
role !== action.payload.role);
return { ...state, roles };
}
case 'SET_PICTURE':
return { ...state, picture: action.payload.picture };
@ -100,13 +71,6 @@ const me = (state = initialState, action) =>
return { ...state, audioDevices: devices };
}
case 'SET_AUDIO_OUTPUT_DEVICES':
{
const { devices } = action.payload;
return { ...state, audioOutputDevices: devices };
}
case 'SET_WEBCAM_DEVICES':
{
const { devices } = action.payload;
@ -135,18 +99,18 @@ const me = (state = initialState, action) =>
return { ...state, screenShareInProgress: flag };
}
case 'SET_RAISED_HAND':
case 'SET_MY_RAISE_HAND_STATE':
{
const { flag } = action.payload;
return { ...state, raisedHand: flag };
return { ...state, raiseHand: flag };
}
case 'SET_RAISED_HAND_IN_PROGRESS':
case 'SET_MY_RAISE_HAND_STATE_IN_PROGRESS':
{
const { flag } = action.payload;
return { ...state, raisedHandInProgress: flag };
return { ...state, raiseHandInProgress: flag };
}
case 'SET_DISPLAY_NAME_IN_PROGRESS':
@ -156,20 +120,6 @@ const me = (state = initialState, action) =>
return { ...state, displayNameInProgress: flag };
}
case 'SET_IS_SPEAKING':
{
const { flag } = action.payload;
return { ...state, isSpeaking: flag };
}
case 'SET_AUTO_MUTED':
{
const { flag } = action.payload;
return { ...state, isAutoMuted: flag };
}
default:
return state;
}

View File

@ -10,13 +10,13 @@ const peerVolumes = (state = initialState, action) =>
peerId
} = action.payload;
return { ...state, [peerId]: -100 };
return { ...state, [peerId]: 0 };
}
case 'ADD_PEER':
{
const { peer } = action.payload;
return { ...state, [peer.id]: -100 };
return { ...state, [peer.id]: 0 };
}
case 'REMOVE_PEER':
@ -31,10 +31,9 @@ const peerVolumes = (state = initialState, action) =>
case 'SET_PEER_VOLUME':
{
const { peerId } = action.payload;
const dBs = action.payload.volume < -100 ? -100 : action.payload.volume;
const { peerId, volume } = action.payload;
return { ...state, [peerId]: Math.round(dBs) };
return { ...state, [peerId]: volume };
}
default:

View File

@ -1,6 +1,4 @@
const initialState = {};
const peer = (state = initialState, action) =>
const peer = (state = {}, action) =>
{
switch (action.type)
{
@ -19,21 +17,8 @@ const peer = (state = initialState, action) =>
case 'SET_PEER_SCREEN_IN_PROGRESS':
return { ...state, peerScreenInProgress: action.payload.flag };
case 'SET_PEER_KICK_IN_PROGRESS':
return { ...state, peerKickInProgress: action.payload.flag };
case 'SET_PEER_RAISED_HAND':
return {
...state,
raisedHand : action.payload.raisedHand,
raisedHandTimestamp : action.payload.raisedHandTimestamp
};
case 'SET_PEER_RAISED_HAND_IN_PROGRESS':
return {
...state,
raisedHandInProgress : action.payload.flag
};
case 'SET_PEER_RAISE_HAND_STATE':
return { ...state, raiseHandState: action.payload.raiseHandState };
case 'ADD_CONSUMER':
{
@ -55,44 +40,12 @@ const peer = (state = initialState, action) =>
return { ...state, picture: action.payload.picture };
}
case 'ADD_PEER_ROLE':
{
const roles = [ ...state.roles, action.payload.role ];
return { ...state, roles };
}
case 'REMOVE_PEER_ROLE':
{
const roles = state.roles.filter((role) =>
role !== action.payload.role);
return { ...state, roles };
}
case 'STOP_PEER_AUDIO_IN_PROGRESS':
return {
...state,
stopPeerAudioInProgress : action.payload.flag
};
case 'STOP_PEER_VIDEO_IN_PROGRESS':
return {
...state,
stopPeerVideoInProgress : action.payload.flag
};
case 'STOP_PEER_SCREEN_SHARING_IN_PROGRESS':
return {
...state,
stopPeerScreenSharingInProgress : action.payload.flag
};
default:
return state;
}
};
const peers = (state = initialState, action) =>
const peers = (state = {}, action) =>
{
switch (action.type)
{
@ -115,15 +68,9 @@ const peers = (state = initialState, action) =>
case 'SET_PEER_VIDEO_IN_PROGRESS':
case 'SET_PEER_AUDIO_IN_PROGRESS':
case 'SET_PEER_SCREEN_IN_PROGRESS':
case 'SET_PEER_RAISED_HAND':
case 'SET_PEER_RAISED_HAND_IN_PROGRESS':
case 'SET_PEER_RAISE_HAND_STATE':
case 'SET_PEER_PICTURE':
case 'ADD_CONSUMER':
case 'ADD_PEER_ROLE':
case 'REMOVE_PEER_ROLE':
case 'STOP_PEER_AUDIO_IN_PROGRESS':
case 'STOP_PEER_VIDEO_IN_PROGRESS':
case 'STOP_PEER_SCREEN_SHARING_IN_PROGRESS':
{
const oldPeer = state[action.payload.peerId];
@ -135,7 +82,6 @@ const peers = (state = initialState, action) =>
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
}
case 'SET_PEER_KICK_IN_PROGRESS':
case 'REMOVE_CONSUMER':
{
const oldPeer = state[action.payload.peerId];
@ -147,11 +93,6 @@ const peers = (state = initialState, action) =>
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
}
case 'CLEAR_PEERS':
{
return initialState;
}
default:
return state;
}

View File

@ -60,17 +60,6 @@ const producers = (state = initialState, action) =>
return { ...state, [producerId]: newProducer };
}
case 'SET_PRODUCER_SCORE':
{
const { producerId, score } = action.payload;
const producer = state[producerId];
const newProducer = { ...producer, score };
return { ...state, [producerId]: newProducer };
}
default:
return state;
}

View File

@ -1,40 +1,24 @@
const initialState =
{
name : '',
// new/connecting/connected/disconnected/closed,
state : 'new',
state : 'new', // new/connecting/connected/disconnected/closed,
locked : false,
inLobby : false,
signInRequired : false,
overRoomLimit : false,
// access code to the room if locked and joinByAccessCode == true
accessCode : '',
// if true: accessCode is a possibility to open the room
joinByAccessCode : true,
accessCode : '', // access code to the room if locked and joinByAccessCode == true
joinByAccessCode : true, // if true: accessCode is a possibility to open the room
activeSpeakerId : null,
torrentSupport : false,
showSettings : false,
fullScreenConsumer : null, // ConsumerID
windowConsumer : null, // ConsumerID
toolbarsVisible : true,
mode : window.config.defaultLayout || 'democratic',
mode : 'democratic',
selectedPeerId : null,
spotlights : [],
settingsOpen : false,
extraVideoOpen : false,
helpOpen : false,
aboutOpen : false,
currentSettingsTab : 'media', // media, appearence, advanced
lockDialogOpen : false,
joined : false,
muteAllInProgress : false,
lobbyPeersPromotionInProgress : false,
stopAllVideoInProgress : false,
closeMeetingInProgress : false,
clearChatInProgress : false,
clearFileSharingInProgress : false,
roomPermissions : null,
allowWhenRoleMissing : null
joined : false
};
const room = (state = initialState, action) =>
@ -81,12 +65,7 @@ const room = (state = initialState, action) =>
return { ...state, signInRequired };
}
case 'SET_OVER_ROOM_LIMIT':
{
const { overRoomLimit } = action.payload;
return { ...state, overRoomLimit };
}
case 'SET_ACCESS_CODE':
{
const { accessCode } = action.payload;
@ -115,34 +94,6 @@ const room = (state = initialState, action) =>
return { ...state, settingsOpen };
}
case 'SET_EXTRA_VIDEO_OPEN':
{
const { extraVideoOpen } = action.payload;
return { ...state, extraVideoOpen };
}
case 'SET_HELP_OPEN':
{
const { helpOpen } = action.payload;
return { ...state, helpOpen };
}
case 'SET_ABOUT_OPEN':
{
const { aboutOpen } = action.payload;
return { ...state, aboutOpen };
}
case 'SET_SETTINGS_TAB':
{
const { tab } = action.payload;
return { ...state, currentSettingsTab: tab };
}
case 'SET_ROOM_ACTIVE_SPEAKER':
{
const { peerId } = action.payload;
@ -159,7 +110,7 @@ const room = (state = initialState, action) =>
case 'TOGGLE_JOINED':
{
const joined = true;
const joined = !state.joined;
return { ...state, joined };
}
@ -212,46 +163,6 @@ const room = (state = initialState, action) =>
return { ...state, spotlights };
}
case 'CLEAR_SPOTLIGHTS':
{
return { ...state, spotlights: [] };
}
case 'SET_LOBBY_PEERS_PROMOTION_IN_PROGRESS':
return { ...state, lobbyPeersPromotionInProgress: action.payload.flag };
case 'MUTE_ALL_IN_PROGRESS':
return { ...state, muteAllInProgress: action.payload.flag };
case 'STOP_ALL_VIDEO_IN_PROGRESS':
return { ...state, stopAllVideoInProgress: action.payload.flag };
case 'STOP_ALL_SCREEN_SHARING_IN_PROGRESS':
return { ...state, stopAllScreenSharingInProgress: action.payload.flag };
case 'CLOSE_MEETING_IN_PROGRESS':
return { ...state, closeMeetingInProgress: action.payload.flag };
case 'CLEAR_CHAT_IN_PROGRESS':
return { ...state, clearChatInProgress: action.payload.flag };
case 'CLEAR_FILE_SHARING_IN_PROGRESS':
return { ...state, clearFileSharingInProgress: action.payload.flag };
case 'SET_ROOM_PERMISSIONS':
{
const { roomPermissions } = action.payload;
return { ...state, roomPermissions };
}
case 'SET_ALLOW_WHEN_ROLE_MISSING':
{
const { allowWhenRoleMissing } = action.payload;
return { ...state, allowWhenRoleMissing };
}
default:
return state;
}

View File

@ -11,16 +11,14 @@ import chat from './chat';
import toolarea from './toolarea';
import files from './files';
import settings from './settings';
import transports from './transports';
export default combineReducers({
room,
me,
producers,
consumers,
transports,
peers,
lobbyPeers,
consumers,
peerVolumes,
notifications,
chat,

View File

@ -4,28 +4,9 @@ const initialState =
selectedWebcam : null,
selectedAudioDevice : null,
advancedMode : false,
sampleRate : 48000,
channelCount : 1,
volume : 1.0,
autoGainControl : false,
echoCancellation : true,
noiseSuppression : true,
voiceActivatedUnmute : false,
noiseThreshold : -50,
sampleSize : 16,
// low, medium, high, veryhigh, ultra
resolution : window.config.defaultResolution || 'medium',
frameRate : window.config.defaultFrameRate || 15,
screenSharingResolution : window.config.defaultScreenResolution || 'veryhigh',
screenSharingFrameRate : window.config.defaultScreenSharingFrameRate || 5,
resolution : 'medium', // low, medium, high, veryhigh, ultra
lastN : 4,
permanentTopBar : true,
hiddenControls : false,
showNotifications : true,
notificationSounds : true,
buttonControlBar : window.config.buttonControlBar || false,
drawerOverlayed : window.config.drawerOverlayed || true,
...window.config.defaultAudio
permanentTopBar : true
};
const settings = (state = initialState, action) =>
@ -42,11 +23,6 @@ const settings = (state = initialState, action) =>
return { ...state, selectedAudioDevice: action.payload.deviceId };
}
case 'CHANGE_AUDIO_OUTPUT_DEVICE':
{
return { ...state, selectedAudioOutputDevice: action.payload.deviceId };
}
case 'SET_DISPLAY_NAME':
{
const { displayName } = action.payload;
@ -61,76 +37,6 @@ const settings = (state = initialState, action) =>
return { ...state, advancedMode };
}
case 'SET_SAMPLE_RATE':
{
const { sampleRate } = action.payload;
return { ...state, sampleRate };
}
case 'SET_CHANNEL_COUNT':
{
const { channelCount } = action.payload;
return { ...state, channelCount };
}
case 'SET_VOLUME':
{
const { volume } = action.payload;
return { ...state, volume };
}
case 'SET_AUTO_GAIN_CONTROL':
{
const { autoGainControl } = action.payload;
return { ...state, autoGainControl };
}
case 'SET_ECHO_CANCELLATION':
{
const { echoCancellation } = action.payload;
return { ...state, echoCancellation };
}
case 'SET_NOISE_SUPPRESSION':
{
const { noiseSuppression } = action.payload;
return { ...state, noiseSuppression };
}
case 'SET_VOICE_ACTIVATED_UNMUTE':
{
const { voiceActivatedUnmute } = action.payload;
return { ...state, voiceActivatedUnmute };
}
case 'SET_NOISE_THRESHOLD':
{
const { noiseThreshold } = action.payload;
return { ...state, noiseThreshold };
}
case 'SET_DEFAULT_AUDIO':
{
const { audio } = action.payload;
return { ...state, audio };
}
case 'SET_SAMPLE_SIZE':
{
const { sampleSize } = action.payload;
return { ...state, sampleSize };
}
case 'SET_LAST_N':
{
const { lastN } = action.payload;
@ -145,41 +51,6 @@ const settings = (state = initialState, action) =>
return { ...state, permanentTopBar };
}
case 'TOGGLE_BUTTON_CONTROL_BAR':
{
const buttonControlBar = !state.buttonControlBar;
return { ...state, buttonControlBar };
}
case 'TOGGLE_DRAWER_OVERLAYED':
{
const drawerOverlayed = !state.drawerOverlayed;
return { ...state, drawerOverlayed };
}
case 'TOGGLE_HIDDEN_CONTROLS':
{
const hiddenControls = !state.hiddenControls;
return { ...state, hiddenControls };
}
case 'TOGGLE_NOTIFICATION_SOUNDS':
{
const notificationSounds = !state.notificationSounds;
return { ...state, notificationSounds };
}
case 'TOGGLE_SHOW_NOTIFICATIONS':
{
const showNotifications = !state.showNotifications;
return { ...state, showNotifications };
}
case 'SET_VIDEO_RESOLUTION':
{
const { resolution } = action.payload;
@ -187,27 +58,6 @@ const settings = (state = initialState, action) =>
return { ...state, resolution };
}
case 'SET_VIDEO_FRAME_RATE':
{
const { frameRate } = action.payload;
return { ...state, frameRate };
}
case 'SET_SCREEN_SHARING_RESOLUTION':
{
const { screenSharingResolution } = action.payload;
return { ...state, screenSharingResolution };
}
case 'SET_SCREEN_SHARING_FRAME_RATE':
{
const { screenSharingFrameRate } = action.payload;
return { ...state, screenSharingFrameRate };
}
default:
return state;
}

View File

@ -1,19 +0,0 @@
const initialState = {};
const transports = (state = initialState, action) =>
{
switch (action.type)
{
case 'ADD_TRANSPORT_STATS':
{
const { transport, type } = action.payload;
return { ...state, [type]: transport[0] };
}
default:
return state;
}
};
export default transports;

View File

@ -49,26 +49,6 @@
"room.spotlights": "Spotlight中的参与者",
"room.passive": "被动参与者",
"room.videoPaused": "该视频已暂停",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "登录",
"tooltip.logout": "注销",
@ -80,15 +60,6 @@
"tooltip.lobby": "显示大厅",
"tooltip.settings": "显示设置",
"tooltip.participants": "显示参加者",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"room.stopAllScreenSharing": null,
"label.roomName": "房间名称",
"label.chooseRoomButton": "继续",
@ -102,7 +73,6 @@
"label.filesharing": "文件共享",
"label.participants": "参与者",
"label.shareFile": "共享文件",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "不支持文件共享",
"label.unknown": "未知",
"label.democratic": "民主视图",
@ -113,13 +83,6 @@
"label.veryHigh": "非常高 (FHD)",
"label.ultra": "超高 (UHD)",
"label.close": "关闭",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "设置",
"settings.camera": "视频设备",
@ -128,30 +91,12 @@
"settings.audio": "音频设备",
"settings.selectAudio": "选择音频设备",
"settings.cantSelectAudio": "无法选择音频设备",
"settings.audioOutput": "音频输出设备",
"settings.selectAudioOutput": "选择音频输出设备",
"settings.cantSelectAudioOutput": "无法选择音频输出设备",
"settings.resolution": "选择视频分辨率",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "房间布局",
"settings.selectRoomLayout": "选择房间布局",
"settings.advancedMode": "高级模式",
"settings.permanentTopBar": "永久顶吧",
"settings.lastn": "可见视频数量",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "无法保存文件",
"filesharing.startingFileShare": "正在尝试共享文件",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "麦克风已断开",
"devices.microphoneError": "麦克风发生错误",
"devices.microphoneMute": "麦克风静音",
"devices.microphoneUnMute": "取消麦克风静音",
"devices.microPhoneMute": "麦克风静音",
"devices.micophoneUnMute": "取消麦克风静音",
"devices.microphoneEnable": "启用了麦克风",
"devices.microphoneMuteError": "无法使麦克风静音",
"devices.microphoneUnMuteError": "无法取消麦克风静音",
@ -191,15 +136,5 @@
"devices.screenSharingError": "访问屏幕时发生错误",
"devices.cameraDisconnected": "相机已断开连接",
"devices.cameraError": "访问相机时发生错误",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "访问相机时发生错误"
}

View File

@ -48,27 +48,6 @@
"room.spotlights": "Aktivní Účastníci",
"room.passive": "Pasivní Účastníci",
"room.videoPaused": "Toto video bylo pozastaveno",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Přihlášení",
"tooltip.logout": "Odhlášení",
@ -79,15 +58,6 @@
"tooltip.leaveFullscreen": "Vypnout režim celé obrazovky (fullscreen)",
"tooltip.lobby": "Ukázat Přijímací místnost",
"tooltip.settings": "Zobrazit nastavení",
"tooltip.participants": null,
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Jméno místnosti",
"label.chooseRoomButton": "Pokračovat",
@ -101,7 +71,6 @@
"label.filesharing": "Sdílení souborů",
"label.participants": "Účastníci",
"label.shareFile": "Sdílet soubor",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Sdílení souborů není podporováno",
"label.unknown": "Neznámý",
"label.democratic": "Rozvržení: Demokratické",
@ -112,13 +81,6 @@
"label.veryHigh": "Velmi vysoké (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Zavřít",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Nastavení",
"settings.camera": "Kamera",
@ -127,30 +89,10 @@
"settings.audio": "Audio zařízení",
"settings.selectAudio": "Vyberte audio zařízení",
"settings.cantSelectAudio": "Není možno vybrat audio zařízení",
"settings.audioOutput": "Audio output zařízení",
"settings.selectAudioOutput": "Vyberte audio output zařízení",
"settings.cantSelectAudioOutput": "Není možno vybrat audio output zařízení",
"settings.resolution": "Vyberte rozlišení vašeho videa",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Rozvržení místnosti",
"settings.selectRoomLayout": "Vyberte rozvržení místnosti",
"settings.advancedMode": "Pokočilý mód",
"settings.permanentTopBar": null,
"settings.lastn": null,
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Není možné uložit soubor",
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
@ -180,8 +122,8 @@
"devices.microphoneDisconnected": "Mikrofon odpojen",
"devices.microphoneError": "Při přístupu k vašemu mikrofonu se vyskytla chyba",
"devices.microphoneMute": "Mikrofon ztišen",
"devices.microphoneUnMute": "Ztišení mikrofonu zrušeno",
"devices.microPhoneMute": "Mikrofon ztišen",
"devices.micophoneUnMute": "Ztišení mikrofonu zrušeno",
"devices.microphoneEnable": "Mikrofon povolen",
"devices.microphoneMuteError": "Není možné ztišit váš mikrofon",
"devices.microphoneUnMuteError": "Není možné zrušit ztišení vašeho mikrofonu",
@ -190,15 +132,5 @@
"devices.screenSharingError": "Při přístupu k vaší obrazovce se vyskytla chyba",
"devices.cameraDisconnected": "Kamera odpojena",
"devices.cameraError": "Při přístupu k vaší kameře se vyskytla chyba",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Při přístupu k vaší kameře se vyskytla chyba"
}

View File

@ -1,46 +1,46 @@
{
"socket.disconnected": "Verbindung unterbrochen",
"socket.reconnecting": "Verbindung unterbrochen, versuche neu zu verbinden",
"socket.reconnected": "Verbindung wiederhergestellt",
"socket.reconnected": "Verbindung wieder herges|tellt",
"socket.requestError": "Fehler bei Serveranfrage",
"room.chooseRoom": "Wähle den Raum aus, den du betreten möchtest",
"room.chooseRoom": "Wähle den Namen für den Raum, den du betreten möchtest",
"room.cookieConsent": "Diese Seite verwendet Cookies, um die Benutzerfreundlichkeit zu erhöhen",
"room.consentUnderstand": "Verstanden",
"room.joined": "Du bist dem Raum beigetreten",
"room.consentUnderstand": "Ich stimme zu",
"room.joined": "Konferenzraum betreten",
"room.cantJoin": "Betreten des Raumes nicht möglich",
"room.youLocked": "Du hast den Raum abgeschlossen",
"room.youLocked": "Raum wurde abgeschlossen",
"room.cantLock": "Abschließen des Raumes nicht möglich",
"room.youUnLocked": "Du hast den Raum geöffnet",
"room.youUnLocked": "Raum geöffnet",
"room.cantUnLock": "Öffnen des Raumes nicht möglich",
"room.locked": "Raum wurde abgeschlossen",
"room.unlocked": "Raum wurde geöffnet",
"room.newLobbyPeer": "Neuer Teilnehmer im Warteraum",
"room.lobbyPeerLeft": "Ein Teilnehmer hat den Warteraum verlassen",
"room.lobbyPeerChangedDisplayName": "Ein Teilnehmer im Warteraum hat seinen Namen geändert zu: {displayName}",
"room.lobbyPeerChangedPicture": "Ein Teilnehmer im Warteraum hat seinen Avatar geändert",
"room.setAccessCode": "Zugangscode für den Raum geändert",
"room.accessCodeOn": "Zugangscode aktiviert",
"room.accessCodeOff": "Zugangscode deaktiviert",
"room.newLobbyPeer": "Neuer Teilnehmer im Empfangsraum",
"room.lobbyPeerLeft": "Teilnehmer hat Empfangsraum verlassen",
"room.lobbyPeerChangedDisplayName": "Teilnehmer im Empfangsraum hat seinen Namen geändert: {displayName}",
"room.lobbyPeerChangedPicture": "Teilnehmer in Empfangsraum hat sein Avatar geändert",
"room.setAccessCode": "Zugangskode für den Raum geändert",
"room.accessCodeOn": "Zugangskode aktiviert",
"room.accessCodeOff": "Zugangskode deaktiviert",
"room.peerChangedDisplayName": "{oldDisplayName} heißt jetzt {displayName}",
"room.newPeer": "{displayName} hat den Raum betreten",
"room.newFile": "Neue Datei verfügbar",
"room.toggleAdvancedMode": "Erweiterter Modus aktiv",
"room.setDemocraticView": "Demokratische Ansicht",
"room.setFilmStripView": "Filmstreifen-Ansicht",
"room.setDemocraticView": "Raumlayout demokratisch",
"room.setFilmStripView": "Raumlayout Filmstreifen",
"room.loggedIn": "Angemeldet",
"room.loggedOut": "Abgemeldet",
"room.changedDisplayName": "Dein Name ist jetzt {displayName}",
"room.changeDisplayNameError": "Dein Name konnte nicht geändert werden",
"room.chatError": "Die Chat-Nachricht konnte nicht gesendet werden",
"room.aboutToJoin": "Du bist dabei, folgenden Raum zu betreten:",
"room.changeDisplayNameError": "Konnte Name nicht ändern",
"room.chatError": "Konnte Meldung nicht senden",
"room.aboutToJoin": "Du bist dabei den Raum zu betreten",
"room.roomId": "Raum ID: {roomName}",
"room.setYourName": "Gib deinen Namen an und wähle aus, wie du den Raum betreten willst:",
"room.setYourName": "Gib deinen Namen an und wähle wie den Raum betreten willst",
"room.audioOnly": "Nur Audio",
"room.audioVideo": "Audio und Video",
"room.youAreReady": "Ok, du bist bereit",
"room.emptyRequireLogin": "Der Raum ist leer. Melde dich an um die Konferenz zu starten oder warte bis der Raum aktiviert wird",
"room.locketWait": "Der Raum ist abgeschlossen - warte, bis dich jemand rein lässt...",
"room.youAreReady": "Ok, Du bist bereit",
"room.emptyRequireLogin": "Der Raum ist leer. Melde dich an um den Raum zu aktivieren, oder warte bis der Raum aktiviert wird",
"room.locketWait": "Der Raum ist abgeschlossen, warte bis Dir jemand öffnet",
"room.lobbyAdministration": "Warteraum",
"room.peersInLobby": "Teilnehmer im Warteraum",
"room.lobbyEmpty": "Der Warteraum ist leer",
@ -49,157 +49,92 @@
"room.spotlights": "Aktive Teinehmer",
"room.passive": "Passive Teilnehmer",
"room.videoPaused": "Video gestoppt",
"room.muteAll": "Alle stummschalten",
"room.stopAllVideo": "Alle Videos stoppen",
"room.closeMeeting": "Meeting beenden",
"room.clearChat": "Liste löschen",
"room.clearFileSharing": "Liste löschen",
"room.speechUnsupported": "Dein Browser unterstützt keine Spracherkennung",
"room.moderatoractions": "Moderator Aktionen",
"room.raisedHand": "{displayName} hebt die Hand",
"room.loweredHand": "{displayName} senkt die Hand",
"room.extraVideo": "Video hinzufügen",
"room.overRoomLimit": "Der Raum ist voll, probiere es später nochmal",
"room.help": "Hilfe",
"room.about": "Über",
"room.shortcutKeys": "Tastaturkürzel",
"room.browsePeersSpotlight": "Teilnehmer durchgehen und anzeigen",
"room.stopAllScreenSharing": "Stoppe alle Bildschirmfreigaben",
"me.mutedPTT": "Du bist stummgeschaltet. Halte die LEERTASTE um zu sprechen",
"roles.gotRole": "Rolle erhalten: {role}",
"roles.lostRole": "Rolle entzogen: {role}",
"tooltip.login": "Anmelden",
"tooltip.logout": "Abmelden",
"tooltip.admitFromLobby": "Teilnehmer reinlassen",
"tooltip.admitFromLobby": "Teilnehmer aktivieren",
"tooltip.lockRoom": "Raum abschließen",
"tooltip.unLockRoom": "Raum entsperren",
"tooltip.unLockRoom": "Raum öffnen",
"tooltip.enterFullscreen": "Vollbild",
"tooltip.leaveFullscreen": "Vollbild verlassen",
"tooltip.lobby": "Warteraum",
"tooltip.settings": "Einstellungen",
"tooltip.participants": "Teilnehmer",
"tooltip.kickParticipant": "Rauswerfen",
"tooltip.muteParticipant": "Stummschalten",
"tooltip.muteParticipantVideo": "Video stoppen",
"tooltip.raisedHand": "Hand heben",
"tooltip.muteScreenSharing": "Stoppe Bildschirmfreigabe",
"tooltip.muteParticipantAudioModerator": "Teilnehmer-Audio global stummschalten",
"tooltip.muteParticipantVideoModerator": "Teilnehmer-Video global stoppen",
"tooltip.muteScreenSharingModerator": "Teilnehmer-Bildschirmfreigabe global beenden",
"label.roomName": "Name des Raums",
"label.roomName": "Name des Raumes",
"label.chooseRoomButton": "Weiter",
"label.yourName": "Dein Name",
"label.newWindow": "Neues Fenster",
"label.newWindow": "In separatem Fenster öffnen",
"label.fullscreen": "Vollbild",
"label.openDrawer": "Menü",
"label.leave": "Verlassen",
"label.chatInput": "Schreibe eine Nachricht...",
"label.leave": "Ausgang",
"label.chatInput": "Schreibe Chat...",
"label.chat": "Chat",
"label.filesharing": "Dateien",
"label.participants": "Teilnehmer",
"label.shareFile": "Datei hochladen",
"label.shareGalleryFile": "Bild teilen",
"label.shareFile": "Teile Datai",
"label.fileSharingUnsupported": "Dateifreigabe nicht unterstützt",
"label.unknown": "Unbekannt",
"label.democratic": "Demokratisch",
"label.filmstrip": "Filmstreifen",
"label.low": "Niedrig",
"label.medium": "Mittel",
"label.medium": "Medium",
"label.high": "Hoch (HD)",
"label.veryHigh": "Sehr hoch (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Schließen",
"label.media": "Audio / Video",
"label.appearance": "Ansicht",
"label.advanced": "Erweitert",
"label.addVideo": "Video hinzufügen",
"label.promoteAllPeers": "Alle Teilnehmer reinlassen",
"label.moreActions": "Weitere Aktionen",
"label.version": "Version",
"settings.settings": "Einstellungen",
"settings.camera": "Kamera",
"settings.selectCamera": "Wähle ein Videogerät",
"settings.selectCamera": "Wähle Videogerät",
"settings.cantSelectCamera": "Kann Videogerät nicht aktivieren",
"settings.audio": "Audiogerät",
"settings.selectAudio": "Wähle ein Audiogerät",
"settings.selectAudio": "Wähle Audiogerät",
"settings.cantSelectAudio": "Kann Audiogerät nicht aktivieren",
"settings.audioOutput": "Audioausgabegerät",
"settings.selectAudioOutput": "Wähle ein Audioausgabegerät",
"settings.cantSelectAudioOutput": "Kann Audioausgabegerät nicht aktivieren",
"settings.resolution": "Wähle eine Auflösung",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.resolution": "Wähle Auflösung",
"settings.layout": "Raumlayout",
"settings.selectRoomLayout": "Wähle ein Raumlayout",
"settings.selectRoomLayout": "Wähle Raumlayout",
"settings.advancedMode": "Erweiterter Modus",
"settings.permanentTopBar": "Permanente obere Leiste",
"settings.lastn": "Anzahl der sichtbaren Videos",
"settings.hiddenControls": "Medienwerkzeugleiste automatisch ausblenden",
"settings.notificationSounds": "Audiosignal bei Benachrichtigungen",
"settings.showNotifications": "Zeige Benachrichtigungen",
"settings.buttonControlBar": "Separate seitliche Medienwerkzeugleiste",
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": "Erweiterte Audio-Einstellungen",
"settings.echoCancellation": "Echounterdrückung",
"settings.autoGainControl": "Automatische Pegelregelung (Audioeingang)",
"settings.noiseSuppression": "Rauschunterdrückung",
"settings.drawerOverlayed": "Seitenpanel verdeckt Hauptinhalt",
"settings.voiceActivatedUnmute": "Automatische Spracherkennung",
"settings.noiseThreshold": "Geräuschepegel",
"filesharing.saveFileError": "Fehler beim Speichern der Datei",
"filesharing.startingFileShare": "Starte Teilen der Datei",
"filesharing.successfulFileShare": "Datei wurde geteilt",
"filesharing.unableToShare": "Datei kann nicht geteilt werden",
"filesharing.unableToShare": "Kann Datei nicht teilen",
"filesharing.error": "Fehler beim Teilen der Datei",
"filesharing.finished": "Datei heruntergeladen",
"filesharing.save": "Speichern",
"filesharing.sharedFile": "{displayName} hat eine Datei geteilt",
"filesharing.download": "Herunterladen",
"filesharing.missingSeeds": "Wenn der Download zu lange dauert, ist wahrscheinlich keiner mehr im Raum, der die Datei teilen kann. Die Datei muss erneut hochgeladen werden.",
"filesharing.missingSeeds": "Wenn das Herunterladen nicht pausiert, ist wahrscheinlich niemand mehr im Raum, der die Datei teilen kann. Datei muss erneut geteilt werden.",
"devices.devicesChanged": "Mediengeräte wurden aktualisiert und sind in den Einstellungen verfügbar",
"devices.devicesChanged": "Mediengeräte wurden aktualisiert und sind in Einstellungen verfügbar",
"device.audioUnsupported": "Audio nicht unterstützt",
"device.activateAudio": "Aktiviere Audio",
"device.muteAudio": "Stummschalten",
"device.unMuteAudio": "Stummschaltung aufheben",
"device.muteAudio": "stummschalten",
"device.unMuteAudio": "Aktiviere Audio",
"device.videoUnsupported": "Video nicht unterstützt",
"device.startVideo": "Starte Video",
"device.stopVideo": "Stoppe Video",
"device.screenSharingUnsupported": "Bildschirmfreigabe nicht unterstützt",
"device.startScreenSharing": "Starte Bildschirmfreigabe",
"device.stopScreenSharing": "Beende Bildschirmfreigabe",
"device.screenSharingUnsupported": "Bildschirmteilen nicht unterstützt",
"device.startScreenSharing": "Bildschirmteilen",
"device.stopScreenSharing": "Beende Bildschirmteilen",
"devices.microphoneDisconnected": "Mikrofon nicht verbunden",
"devices.microphoneError": "Fehler beim Zugriff auf dein Mikrofon",
"devices.microphoneMute": "Mikrofon stummgeschaltet",
"devices.microphoneUnMute": "Mikrofon aktiviert",
"devices.microphoneEnable": "Mikrofon aktiviert",
"devices.microphoneMuteError": "Kann Mikrofon nicht stummschalten",
"devices.microphoneUnMuteError": "Kann Mikrofon nicht aktivieren",
"devices.microphoneDisconnected": "Mikrophon nicht verbunden",
"devices.microphoneError": "Fehler mit Mikrophon",
"devices.microPhoneMute": "Mikrophon stumm geschaltet",
"devices.micophoneUnMute": "Mikrophon aktiviert",
"devices.microphoneEnable": "Mikrofonen aktiviert",
"devices.microphoneMuteError": "Kann Mikrophon nicht stummschalten",
"devices.microphoneUnMuteError": "Kann Mikrophon nicht aktivieren",
"devices.screenSharingDisconnected" : "Bildschirmfreigabe unterbrochen",
"devices.screenSharingError": "Fehler bei der Bildschirmfreigabe",
"devices.screenSharingDisconnected" : "Bildschirmteilen unterbrochen",
"devices.screenSharingError": "Fehler beim Bildschirmteilen",
"devices.cameraDisconnected": "Kamera getrennt",
"devices.cameraError": "Fehler mit deiner Kamera",
"moderator.clearChat": "Moderator hat Chat gelöscht",
"moderator.clearFiles": "Moderator hat geteilte Dateiliste gelöscht",
"moderator.muteAudio": "Moderator hat dich stummgeschaltet",
"moderator.muteVideo": "Moderator hat dein Video gestoppt",
"moderator.stopScreenSharing": "Moderator hat deine Bildschirmfreigabe gestoppt",
"unsupportedBrowser.titleUnsupportedBrowser": "Browser wird nicht unterstützt!",
"unsupportedBrowser.titlewebrtcUnavailable": "Benötigte Funktionen sind in deinem Browser nicht verfügbar!",
"unsupportedBrowser.bodyText": "Dieser Konferenz-Dienst benötigt Funktionen die von deinem Browser nicht unterstützt werden. Bitte aktualisiere deinen Browser, wechsle den Browser oder überprüfe die Browser-Einstellungen. Unterstützte Browser:"
"devices.cameraDisconnected": "Video unterbrochen",
"devices.cameraError": "Fehler mit Videogerät"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Deltagere i fokus",
"room.passive": "Passive deltagere",
"room.videoPaused": "Denne video er sat på pause",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Log ind",
"tooltip.logout": "Log ud",
@ -81,14 +60,6 @@
"tooltip.lobby": "Vis lobby",
"tooltip.settings": "Vis indstillinger",
"tooltip.participants": "Vis deltagere",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Værelsesnavn",
"label.chooseRoomButton": "Fortsæt",
@ -102,7 +73,6 @@
"label.filesharing": "Fildeling",
"label.participants": "Deltagere",
"label.shareFile": "Del fil",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Fildeling er ikke understøttet",
"label.unknown": "Ukendt",
"label.democracy": "Galleri visning",
@ -113,13 +83,6 @@
"label.veryHigh": "Meget høj (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Luk",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Indstillinger",
"settings.camera": "Kamera",
@ -128,30 +91,12 @@
"settings.audio": "Lydenhed",
"settings.selectAudio": "Vælg lydenhed",
"settings.cantSelectAudio": "Kan ikke vælge lydenhed",
"settings.audioOutput": "Audio output enhed",
"settings.selectAudioOutput": "Vælg lydudgangsenhed",
"settings.cantSelectAudioOutput": "Kan ikke vælge lydoutputenhed",
"settings.resolution": "Vælg din videoopløsning",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Møde visning",
"settings.selectRoomLayout": "Vælg møde visning",
"settings.advancedMode": "Avanceret tilstand",
"settings.permanentTopBar": "Permanent øverste linje",
"settings.lastn": "Antal synlige videoer",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Kan ikke gemme fil",
"filesharing.startingFileShare": "Forsøger at dele filen",
@ -161,7 +106,7 @@
"filesharing.finished": "Filen er færdig med at downloade",
"filesharing.save": "Gem",
"filesharing.sharedFile": "{displayName} delte en fil",
"filesharing.download": null,
"filesharing.download": "Download",
"filesharing.missingSeeds": "Hvis denne proces tager lang tid, er der muligvis ikke nogen, der seedede denne torrent. Prøv at bede nogen om at uploade den fil, du ønsker at hente.",
"device.devicesChanged": "Detekteret ndringer i dine enheder, konfigurer dine enheder i indstillingsdialogen",
@ -181,8 +126,8 @@
"device.microphoneDisconnected": "Mikrofon frakoblet",
"device.microphoneError": "Der opstod en fejl under adgang til din mikrofon",
"device.microphoneMute": "Dæmp din mikrofon",
"device.microphoneUnMute": "Slå ikke lyden fra din mikrofon",
"device.microPhoneMute": "Dæmp din mikrofon",
"device.micophoneUnMute": "Slå ikke lyden fra din mikrofon",
"device.microphoneEnable": "Aktiveret din mikrofon",
"device.microphoneMuteError": "Kan ikke slå din mikrofon fra",
"device.microphoneUnMuteError": "Kan ikke slå lyden til på din mikrofon",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Der opstod en fejl ved adgang til skærmdeling",
"device.cameraDisconnected": "Kamera frakoblet",
"device.cameraError": "Der opstod en fejl ved tilkobling af dit kamera",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"device.cameraError": "Der opstod en fejl ved tilkobling af dit kamera"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Συμμετέχοντες στο Spotlight",
"room.passive": "Παθητικοί συμμετέχοντες",
"room.videoPaused": "Το βίντεο έχει σταματήσει",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Σύνδεση",
"tooltip.logout": "Αποσύνδεση",
@ -81,14 +60,6 @@
"tooltip.lobby": "Εμφάνιση λόμπι",
"tooltip.settings": "Εμφάνιση ρυθμίσεων",
"tooltip.participants": "Εμφάνιση συμμετεχόντων",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Όνομα δωματίου",
"label.chooseRoomButton": "Συνέχεια",
@ -102,24 +73,16 @@
"label.filesharing": "Διαμοιρασμοός αρχείου",
"label.participants": "Συμμετέχοντες",
"label.shareFile": "Διαμοιραστείτε ένα αρχείο",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Ο διαμοιρασμός αρχείων δεν υποστηρίζεται",
"label.unknown": "Άγνωστο",
"label.democratic": null,
"label.filmstrip": null,
"label.democratic": "Democratic view",
"label.filmstrip": "Filmstrip view",
"label.low": "Χαμηλή",
"label.medium": "Μέτρια",
"label.high": "Υψηλή (HD)",
"label.veryHigh": "Πολύ υψηλή (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Κλείσιμο",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Ρυθμίσεις",
"settings.camera": "Κάμερα",
@ -128,30 +91,12 @@
"settings.audio": "Συσκευή ήχου",
"settings.selectAudio": "Επιλογή συσκευής ήχου",
"settings.cantSelectAudio": "Αδυναμία επιλογής συσκευής ήχου",
"settings.audioOutput": "Συσκευή εξόδου ήχου",
"settings.selectAudioOutput": "Επιλέξτε συσκευή εξόδου ήχου",
"settings.cantSelectAudioOutput": "Δεν είναι δυνατή η επιλογή συσκευής εξόδου ήχου",
"settings.resolution": "Επιλέξτε την ανάλυση του video",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Περιβάλλον δωματίου",
"settings.selectRoomLayout": "Επιλογή περιβάλλοντος δωματίου",
"settings.advancedMode": "Προηγμένη λειτουργία",
"settings.permanentTopBar": "Μόνιμη μπάρα κορυφής",
"settings.lastn": "Αριθμός ορατών βίντεο",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Αδυναμία αποθήκευσης του αρχείου",
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Το μικρόφωνο αποσυνδέθηκε",
"devices.microphoneError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στο μικρόφωνό σας",
"devices.microphoneMute": "Το μικρόφωνό σας είναι σε σίγαση",
"devices.microphoneUnMute": "Ανοίξτε το μικρόφωνό σας",
"devices.microPhoneMute": "Το μικρόφωνό σας είναι σε σίγαση",
"devices.micophoneUnMute": "Ανοίξτε το μικρόφωνό σας",
"devices.microphoneEnable": "Ενεργοποίησε το μικρόφωνό σας",
"devices.microphoneMuteError": "Δεν είναι δυνατή η σίγαση του μικροφώνου σας",
"devices.microphoneUnMuteError": "Δεν είναι δυνατό το άνοιγμα του μικροφώνου σας",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στην οθόνη σας",
"devices.cameraDisconnected": "Η κάμερα αποσυνδέθηκε",
"devices.cameraError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στην κάμερά σας",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στην κάμερά σας"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Participants in Spotlight",
"room.passive": "Passive Participants",
"room.videoPaused": "This video is paused",
"room.muteAll": "Mute all",
"room.stopAllVideo": "Stop all video",
"room.closeMeeting": "Close meeting",
"room.clearChat": "Clear chat",
"room.clearFileSharing": "Clear files",
"room.speechUnsupported": "Your browser does not support speech recognition",
"room.moderatoractions": "Moderator actions",
"room.raisedHand": "{displayName} raised their hand",
"room.loweredHand": "{displayName} put their hand down",
"room.extraVideo": "Extra video",
"room.overRoomLimit": "The room is full, retry after some time.",
"room.help": "Help",
"room.about": "About",
"room.shortcutKeys": "Shortcut Keys",
"room.browsePeersSpotlight": "Browse participants into Spotlight",
"room.stopAllScreenSharing": "Stop all screen sharing",
"me.mutedPTT": "You are muted, hold down SPACE-BAR to talk",
"roles.gotRole": "You got the role: {role}",
"roles.lostRole": "You lost the role: {role}",
"tooltip.login": "Log in",
"tooltip.logout": "Log out",
@ -81,14 +60,6 @@
"tooltip.lobby": "Show lobby",
"tooltip.settings": "Show settings",
"tooltip.participants": "Show participants",
"tooltip.kickParticipant": "Kick out participant",
"tooltip.muteParticipant": "Mute participant",
"tooltip.muteParticipantVideo": "Mute participant video",
"tooltip.raisedHand": "Raise hand",
"tooltip.muteScreenSharing": "Mute participant share",
"tooltip.muteParticipantAudioModerator": "Mute participant audio globally",
"tooltip.muteParticipantVideoModerator": "Mute participant video globally",
"tooltip.muteScreenSharingModerator": "Mute participant screen share globally",
"label.roomName": "Room name",
"label.chooseRoomButton": "Continue",
@ -102,7 +73,6 @@
"label.filesharing": "File sharing",
"label.participants": "Participants",
"label.shareFile": "Share file",
"label.shareGalleryFile": "Share image",
"label.fileSharingUnsupported": "File sharing not supported",
"label.unknown": "Unknown",
"label.democratic": "Democratic view",
@ -113,13 +83,6 @@
"label.veryHigh": "Very high (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Close",
"label.media": "Media",
"label.appearance": "Appearence",
"label.advanced": "Advanced",
"label.addVideo": "Add video",
"label.promoteAllPeers": "Promote all",
"label.moreActions": "More actions",
"label.version": "Version",
"settings.settings": "Settings",
"settings.camera": "Camera",
@ -128,30 +91,12 @@
"settings.audio": "Audio device",
"settings.selectAudio": "Select audio device",
"settings.cantSelectAudio": "Unable to select audio device",
"settings.audioOutput": "Audio output device",
"settings.selectAudioOutput": "Select audio output device",
"settings.cantSelectAudioOutput": "Unable to select audio output device",
"settings.resolution": "Select your video resolution",
"settings.frameRate": "Select your video frame rate",
"settings.screenSharingResolution": "Select your screen sharing resolution",
"settings.screenSharingFrameRate": "Select your screen sharing frame rate",
"settings.layout": "Room layout",
"settings.selectRoomLayout": "Select room layout",
"settings.advancedMode": "Advanced mode",
"settings.permanentTopBar": "Permanent top bar",
"settings.lastn": "Number of visible videos",
"settings.hiddenControls": "Hidden media controls",
"settings.notificationSounds": "Notification sounds",
"settings.showNotifications": "Show notifications",
"settings.buttonControlBar": "Separate media controls",
"settings.showAdvancedVideo": "Advanced video settings",
"settings.showAdvancedAudio": "Advanced audio settings",
"settings.echoCancellation": "Echo cancellation",
"settings.autoGainControl": "Auto gain control",
"settings.noiseSuppression": "Noise suppression",
"settings.drawerOverlayed": "Side drawer over content",
"settings.voiceActivatedUnmute": "Voice activated unmute",
"settings.noiseThreshold": "Noise threshold",
"filesharing.saveFileError": "Unable to save file",
"filesharing.startingFileShare": "Attempting to share file",
@ -180,9 +125,9 @@
"device.stopScreenSharing": "Stop screen sharing",
"devices.microphoneDisconnected": "Microphone disconnected",
"devices.microphoneError": "An error occured while accessing your microphone",
"devices.microphoneMute": "Muted your microphone",
"devices.microphoneUnMute": "Unmuted your microphone",
"devices.microphoneError": "An error occurred while accessing your microphone",
"devices.microPhoneMute": "Muted your microphone",
"devices.micophoneUnMute": "Unmuted your microphone",
"devices.microphoneEnable": "Enabled your microphone",
"devices.microphoneMuteError": "Unable to mute your microphone",
"devices.microphoneUnMuteError": "Unable to unmute your microphone",
@ -191,15 +136,5 @@
"devices.screenSharingError": "An error occurred while accessing your screen",
"devices.cameraDisconnected": "Camera disconnected",
"devices.cameraError": "An error occured while accessing your camera",
"moderator.clearChat": "Moderator cleared the chat",
"moderator.clearFiles": "Moderator cleared the files",
"moderator.muteAudio": "Moderator muted your audio",
"moderator.muteVideo": "Moderator muted your video",
"moderator.stopScreenSharing": "Moderator stopped your screen sharing",
"unsupportedBrowser.titleUsnsupportedBrowser": "Detected unsupported browser!",
"unsupportedBrowser.titlewebrtcUnavailable": "Required functionality not available in your browser!",
"unsupportedBrowser.bodyText": "This meeting service requires a functionality that is not supported by your browser. Please upgrade, or switch to a different browser, or check your settings. Supported browsers:"
"devices.cameraError": "An error occurred while accessing your camera"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Participantes destacados",
"room.passive": "Participantes pasivos",
"room.videoPaused": "El vídeo está pausado",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Entrar",
"tooltip.logout": "Salir",
@ -81,14 +60,6 @@
"tooltip.lobby": "Mostrar sala de espera",
"tooltip.settings": "Mostrar ajustes",
"tooltip.participants": "Mostrar participantes",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Nombre de la sala",
"label.chooseRoomButton": "Continuar",
@ -102,7 +73,6 @@
"label.filesharing": "Compartir ficheros",
"label.participants": "Participantes",
"label.shareFile": "Compartir fichero",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Compartir ficheros no está disponible",
"label.unknown": "Desconocido",
"label.democratic": "Vista democrática",
@ -113,13 +83,6 @@
"label.veryHigh": "Muy alta (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Cerrar",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Ajustes",
"settings.camera": "Cámara",
@ -128,30 +91,12 @@
"settings.audio": "Dispositivo de sonido",
"settings.selectAudio": "Seleccione dispositivo de sonido",
"settings.cantSelectAudio": "No ha sido posible seleccionar el dispositivo de sonido",
"settings.audioOutput": "Dispositivo de salida de audio",
"settings.selectAudioOutput": "Seleccionar dispositivo de salida de audio",
"settings.cantSelectAudioOutput": "No se puede seleccionar el dispositivo de salida de audio",
"settings.resolution": "Seleccione su resolución de imagen",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Disposición de la sala",
"settings.selectRoomLayout": "Seleccione la disposición de la sala",
"settings.advancedMode": "Modo avanzado",
"settings.permanentTopBar": "Barra superior permanente",
"settings.lastn": "Cantidad de videos visibles",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
"filesharing.startingFileShare": "Intentando compartir el fichero",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Micrófono desconectado",
"devices.microphoneError": "Hubo un error al acceder a su micrófono",
"devices.microphoneMute": "Desactivar micrófono",
"devices.microphoneUnMute": "Activar micrófono",
"devices.microPhoneMute": "Desactivar micrófono",
"devices.micophoneUnMute": "Activar micrófono",
"devices.microphoneEnable": "Micrófono activado",
"devices.microphoneMuteError": "No ha sido posible desactivar su micrófono",
"devices.microphoneUnMuteError": "No ha sido posible activar su micrófono",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Hubo un error al acceder a su pantalla",
"devices.cameraDisconnected": "Cámara desconectada",
"devices.cameraError": "Hubo un error al acceder a su cámara",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Hubo un error al acceder a su cámara"
}

View File

@ -11,7 +11,7 @@
"room.cantJoin" : " Impossible de rejoindre la salle",
"room.youLocked" : " Vous avez privatisé la salle",
"room.cantLock" : " Impossible de privatiser la salle",
"room.youUnLocked": "Vous avez dé-privatisé la salle",
"room.youUnLocked" : " Vous avez dé-privatiser la salle",
"room.cantUnLock" : " Impossible de dé-privatiser la réunion",
"room.locked" : " La réunion est privée",
"room.unlocked" : " La réunion est publique",
@ -20,8 +20,8 @@
"room.lobbyPeerChangedDisplayName" : " Un participant dans la salle dattente a changé de nom pour {displayName}",
"room.lobbyPeerChangedPicture" : " Un participant dans le hall à changer de photo",
"room.setAccessCode" : " Code daccès à la réunion mis à jour",
"room.accessCodeOn": "Code daccès à la réunion activé",
"room.accessCodeOff": "Code daccès à la réunion désactivé",
"room.accessCodeOn" : " Code daccès à la réunion activée",
"room.accessCodeOff" : " Code daccès à la réunion désactivée",
"room.peerChangedDisplayName" : " {oldDisplayName} est maintenant {displayName}",
"room.newPeer" : " {displayName} a rejoint la salle",
"room.newFile" : " Nouveau fichier disponible",
@ -30,7 +30,7 @@
"room.setFilmStripView" : " Passer en vue vignette",
"room.loggedIn" : " Vous êtes connecté",
"room.loggedOut" : " Vous êtes déconnecté",
"room.changedDisplayName": "Votre nom a changé pour {displayname}",
"room.changedDisplayName" : " Votre nom à changer pour {displayname}",
"room.changeDisplayNameError" : " Une erreur sest produite pour votre changement de nom",
"room.chatError" : " Impossible denvoyer un message",
"room.aboutToJoin" : " Vous allez rejoindre une réunion",
@ -39,7 +39,7 @@
"room.audioOnly" : " Audio uniquement",
"room.audioVideo" : " Audio et Vidéo",
"room.youAreReady" : " Ok, vous êtes prêt",
"room.emptyRequireLogin": "La réunion est vide ! Vous pouvez vous connecter pour commencer la réunion ou attendre qu'un hôte se connecte",
"room.emptyRequireLogin" : " La réunion est vide ! Vous pouvez vous connecter pour commencer la réunion ou attendre qu'un hôte se connecte",
"room.locketWait" : " La réunion est privatisée - attendez que quelquun vous laisse entrer",
"room.lobbyAdministration" : " Administration de la salle dattente",
"room.peersInLobby" : " Participants dans la salle dattente",
@ -49,27 +49,6 @@
"room.spotlights" : " Participants actifs",
"room.passive" : " Participants passifs",
"room.videoPaused" : " La vidéo est en pause",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login" : " Connexion",
"tooltip.logout" : " Déconnexion",
@ -81,14 +60,6 @@
"tooltip.lobby" : " Afficher la salle d'attente",
"tooltip.settings" : " Afficher les paramètres",
"tooltip.participants": "Afficher les participants",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName" : " Nom de la salle",
"label.chooseRoomButton" : " Continuer",
@ -96,13 +67,12 @@
"label.newWindow" : " Nouvelle fenêtre",
"label.fullscreen" : " Plein écran",
"label.openDrawer" : " Ouvrir Drawer",
"label.leave": "Quitter",
"label.leave" : " Quiter",
"label.chatInput" : " Entrer un message",
"label.chat" : " Chat",
"label.filesharing" : " Partage de fichier",
"label.participants" : " Participants",
"label.shareFile" : " Partager un fichier",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported" : " Partage de fichier non supporté",
"label.unknown" : " Inconnu",
"label.democratic" : " Vue démocratique",
@ -113,45 +83,20 @@
"label.veryHigh" : " Très Haute Définition (FHD)",
"label.ultra" : " Ultra Haute Définition",
"label.close" : " Fermer",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings" : " Paramètres",
"settings.camera" : " Caméra",
"settings.selectCamera": "Sélectionnez votre caméra",
"settings.selectCamera" : " Sélectionner votre caméra",
"settings.cantSelectCamera" : " Impossible de sélectionner votre caméra",
"settings.audio" : " Microphone",
"settings.selectAudio": "Sélectionnez votre microphone",
"settings.selectAudio" : " Sélectionner votre microphone",
"settings.cantSelectAudio" : " Impossible de sélectionner votre la caméra",
"settings.audioOutput": "Périphérique de sortie audio",
"settings.selectAudioOutput": "Sélectionnez le périphérique de sortie audio",
"settings.cantSelectAudioOutput": "Impossible de sélectionner le périphérique de sortie audio",
"settings.resolution": "Sélectionnez votre résolution",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.resolution" : " Sélection votre résolution",
"settings.layout" : " Mode d'affichage de la salle",
"settings.selectRoomLayout": "Sélectionnez la présentation de la salle",
"settings.selectRoomLayout" : " Sélectionner l'affiche de la salle",
"settings.advancedMode" : " Mode avancé",
"settings.permanentTopBar": "Barre supérieure permanente",
"settings.lastn": "Nombre de vidéos visibles",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError" : " Impossible d'enregistrer le fichier",
"filesharing.startingFileShare" : " Début du transfert de fichier",
@ -163,14 +108,14 @@
"filesharing.sharedFile" : " {displayName} a partagé un fichier",
"filesharing.download" : " Télécharger",
"filesharing.missingSeeds" : " Si le téléchargement prend trop de temps cest quil ny a peut-être plus personne qui partage ce torrent. Demander à quelquun de repartager le document.",
"devices.devicesChanged": "Vos périphériques ont changé, reconfigurez vos périphériques dans le menu paramètre",
"devices.devicesChanged" : " Vos périphériques ont changé, reconfigurer vos périphériques avec le menu paramètre",
"device.audioUnsupported" : " Microphone non supporté",
"device.activateAudio" : " Activer l'audio",
"device.muteAudio" : " Désactiver l'audio",
"device.unMuteAudio" : " Réactiver l'audio",
"device.videoUnsupported": "Vidéo non supportée",
"device.videoUnsupported" : " Vidéo non supporté",
"device.startVideo" : " Démarrer la vidéo",
"device.stopVideo" : " Arrêter la vidéo",
@ -180,8 +125,8 @@
"devices.microphoneDisconnected" : " Microphone déconnecté",
"devices.microphoneError" : " Une erreur est apparue lors de l'accès à votre microphone",
"devices.microphoneMute": "Désactiver le microphone",
"devices.microphoneUnMute": "Réactiver le microphone",
"devices.microPhoneMute" : " Désactiver le microphone",
"devices.micophoneUnMute" : " Réactiver le microphone",
"devices.microphoneEnable" : " Activer le microphone",
"devices.microphoneMuteError" : " Impossible de désactiver le microphone",
"devices.microphoneUnMuteError" : " Impossible de réactiver le microphone",
@ -190,15 +135,5 @@
"devices.screenSharingError" : " Une erreur est apparue lors de l'accès à votre partage d'écran",
"devices.cameraDisconnected" : " Caméra déconnectée",
"devices.cameraError": "Une erreur est apparue lors de l'accès à votre caméra",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError" : " Une erreur est apparue lors de l'accès à votre caméra"
}

View File

@ -46,30 +46,9 @@
"room.lobbyEmpty": "Trenutno nema nikoga u predvorju",
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
"room.me": "Ja",
"room.spotlights": "Sudionici u središtu pažnje",
"room.spotlights": "Sudionici u fokusu",
"room.passive": "Pasivni sudionici",
"room.videoPaused": "Video pauziran",
"room.muteAll": "Utišaj sve",
"room.stopAllVideo": "Ugasi sve kamere",
"room.closeMeeting": "Završi sastanak",
"room.clearChat": "Izbriši razgovor",
"room.clearFileSharing": "Izbriši dijeljene datoteke",
"room.speechUnsupported": "Vaš preglednik ne podržava prepoznavanje govora",
"room.moderatoractions": "Akcije moderatora",
"room.raisedHand": "{displayName} je podigao ruku",
"room.loweredHand": "{displayName} je spustio ruku",
"room.extraVideo": "Dodatni video",
"room.overRoomLimit": "Soba je popunjena, pokušajte ponovno kasnije.",
"room.help": "Pomoć",
"room.about": "O programu",
"room.shortcutKeys": "Prečaci",
"room.browsePeersSpotlight": "Pretražite sudionike u središtu pažnje",
"room.stopAllScreenSharing": null,
"me.mutedPTT": "Utišani ste, pritisnite i držite SPACE tipku za razgovor",
"roles.gotRole": "Dodijeljena vam je uloga: {role}",
"roles.lostRole": "Uloga: {role} je povučena",
"tooltip.login": "Prijava",
"tooltip.logout": "Odjava",
@ -80,15 +59,7 @@
"tooltip.leaveFullscreen": "Izađi iz punog ekrana",
"tooltip.lobby": "Prikaži predvorje",
"tooltip.settings": "Prikaži postavke",
"tooltip.participants": "Prikaži sudionike",
"tooltip.kickParticipant": "Izbaci sudionika",
"tooltip.muteParticipant": "Utišaj sudionika",
"tooltip.muteParticipantVideo": "Ne prikazuj video sudionika",
"tooltip.raisedHand": "Podigni ruku",
"tooltip.muteScreenSharing": "Ne prikazuj dijeljenje ekrana sudionika",
"tooltip.muteParticipantAudioModerator": "Utišaj sve sudionike",
"tooltip.muteParticipantVideoModerator": "Ne prikazuj video svih sudionika",
"tooltip.muteScreenSharingModerator": "Ne prikazuj dijeljenje ekrana svih sudionika",
"tooltip.participants": "Pokažite sudionike",
"label.roomName": "Naziv sobe",
"label.chooseRoomButton": "Nastavi",
@ -102,7 +73,6 @@
"label.filesharing": "Dijeljenje datoteka",
"label.participants": "Sudionici",
"label.shareFile": "Dijeli datoteku",
"label.shareGalleryFile": "Dijeli sliku",
"label.fileSharingUnsupported": "Dijeljenje datoteka nije podržano",
"label.unknown": "Nepoznato",
"label.democratic":"Demokratski prikaz",
@ -113,13 +83,6 @@
"label.veryHigh": "Vrlo visoka (FHD)",
"label.ultra": "Ultra visoka (UHD)",
"label.close": "Zatvori",
"label.media": "Medij",
"label.appearance": "Prikaz",
"label.advanced": "Napredno",
"label.addVideo": "Dodaj video",
"label.promoteAllPeers": "Promoviraj sve",
"label.moreActions": "Više akcija",
"label.version": null,
"settings.settings": "Postavke",
"settings.camera": "Kamera",
@ -128,30 +91,12 @@
"settings.audio": "Uređaj za zvuk",
"settings.selectAudio": "Odaberi uređaj za zvuk",
"settings.cantSelectAudio": "Nije moguće odabrati uređaj za zvuk",
"settings.audioOutput": "Uređaj za izlaz zvuka",
"settings.selectAudioOutput": "Odaberite izlazni uređaj za zvuk",
"settings.cantSelectAudioOutput": "Nije moguće odabrati izlazni uređaj za zvuk",
"settings.resolution": "Odaberi video rezoluciju",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Način prikaza",
"settings.selectRoomLayout": "Odaberi način prikaza",
"settings.advancedMode": "Napredne mogućnosti",
"settings.permanentTopBar": "Stalna gornja šipka",
"settings.lastn": "Broj vidljivih videozapisa",
"settings.hiddenControls": "Skrivene kontrole medija",
"settings.notificationSounds": "Zvuk obavijesti",
"settings.showNotifications": "Prikaži obavijesti",
"settings.buttonControlBar": "Razdvoji upravljanje medijima",
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": "Poništavanje jeke",
"settings.autoGainControl": "Automatsko upravljanje jačinom zvuka",
"settings.noiseSuppression": "Poništavanje šuma",
"settings.drawerOverlayed": "Bočni izbornik iznad sadržaja",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Nije moguće spremiti datoteku",
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Mikrofon odspojen",
"devices.microphoneError": "Greška prilikom pristupa mikrofonu",
"devices.microphoneMute": "Mikrofon utišan",
"devices.microphoneUnMute": "Mikrofon pojačan",
"devices.microPhoneMute": "Mikrofon utišan",
"devices.micophoneUnMute": "Mikrofon pojačan",
"devices.microphoneEnable": "Mikrofon omogućen",
"devices.microphoneMuteError": "Nije moguće utišati mikrofon",
"devices.microphoneUnMuteError": "Nije moguće pojačati mikrofon",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Greška prilikom pristupa ekranu",
"devices.cameraDisconnected": "Kamera odspojena",
"devices.cameraError": "Greška prilikom pristupa kameri",
"moderator.clearChat": "Moderator je izbrisao razgovor",
"moderator.clearFiles": "Moderator je izbrisao datoteke",
"moderator.muteAudio": "Moderator je utišao tvoj zvuk",
"moderator.muteVideo": "Moderator je zaustavio tvoj video",
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Greška prilikom pristupa kameri"
}

View File

@ -1,24 +1,24 @@
{
"socket.disconnected": "A kapcsolat lebomlott",
"socket.reconnecting": "A kapcsolat lebomlott, újrapróbálkozás",
"socket.reconnected": "Sikeres újrakapcsolódás",
"socket.reconnected": "Sikeres újarkapcsolódás",
"socket.requestError": "Sikertelen szerver lekérés",
"room.chooseRoom": "Válaszd ki a konferenciaszobát",
"room.chooseRoom": "Choose the name of the room you would like to join",
"room.cookieConsent": "Ez a weblap a felhasználói élmény fokozása miatt sütiket használ",
"room.consentUnderstand": "Megértettem",
"room.joined": "Csatlakoztál a konferenciához",
"room.consentUnderstand": "I understand",
"room.joined": "Csatlakozátál a konferenciához",
"room.cantJoin": "Sikertelen csatlakozás a konferenciához",
"room.youLocked": "A konferenciába való belépés letiltva",
"room.cantLock": "Sikertelen a konferenciába való belépés letiltása",
"room.cantLock": "Sikertelen a konferenciaba való belépés letiltása",
"room.youUnLocked": "A konferenciába való belépés engedélyezve",
"room.cantUnLock": "Sikertelen a konferenciába való belépés engedélyezése",
"room.locked": "A konferenciába való belépés letiltva",
"room.unlocked": "A konferenciába való belépés engedélyezve",
"room.newLobbyPeer": "Új részvevő lépett be a konferencia előszobájába",
"room.lobbyPeerLeft": "A konferencia előszobájából a részvevő távozott",
"room.lobbyPeerChangedDisplayName": "Az előszobai résztvevő megváltoztatta a nevét: {displayName}",
"room.lobbyPeerChangedPicture": "Az előszobai résztvevő megváltoztatta a képét",
"room.lobbyPeerChangedDisplayName": "Az előszobai résztvevő meváltoztatta a nevét: {displayName}",
"room.lobbyPeerChangedPicture": "Az előszobai résztvevő meváltoztatta a képét",
"room.setAccessCode": "A konferencia hozzáférési kódja megváltozott",
"room.accessCodeOn": "A konferencia hozzáférési kódja aktiválva",
"room.accessCodeOff": "A konferencia hozzáférési kódka deaktiválva",
@ -39,8 +39,8 @@
"room.audioOnly": "csak Hang",
"room.audioVideo": "Hang és Videó",
"room.youAreReady": "Ok, kész vagy",
"room.emptyRequireLogin": "A konferencia üres! Be kell lépned a konferencia elkezdéséhez, vagy várnod kell amíg a házigazda becsatlakozik.",
"room.locketWait": "Az automatikus belépés tiltva van - Várj amíg valaki beenged ...",
"room.emptyRequireLogin": "A konferencia üres! Be kell lépned a konferecnia elkezdéséhez, vagy várnod kell amíg a házigazda becsatlakozik.",
"room.locketWait": "A konferencia szobába a a belépés tilos - Várj amíg valaki be nem enged ...",
"room.lobbyAdministration": "Előszoba adminisztráció",
"room.peersInLobby": "Résztvevők az előszobában",
"room.lobbyEmpty": "Épp senki sincs a konferencia előszobájában",
@ -49,31 +49,10 @@
"room.spotlights": "Látható résztvevők",
"room.passive": "Passzív résztvevők",
"room.videoPaused": "Ez a videóstream szünetel",
"room.muteAll": "Összes némítása",
"room.stopAllVideo": "Összes video némítása",
"room.closeMeeting": "Konferencia lebontása",
"room.clearChat": "Chat történelem kiürítése",
"room.clearFileSharing": "File megosztás kiürítése",
"room.speechUnsupported": "A böngésződ nem támogatja a hangfelismerést",
"room.moderatoractions": "Moderátor funkciók",
"room.raisedHand": "{displayName} jelentkezik",
"room.loweredHand": "{displayName} leeresztette a kezét",
"room.extraVideo": "Kiegészítő videó",
"room.overRoomLimit": "A konferenciaszoba betelt..",
"room.help": "Segítség",
"room.about": "Névjegy",
"room.shortcutKeys": "Billentyűparancsok",
"room.browsePeersSpotlight": "Résztvevők böngészése",
"room.stopAllScreenSharing": "Összes képernyőmegosztás leállítása",
"me.mutedPTT": "Némítva vagy, ha beszélnél nyomd le a SZÓKÖZ billentyűt",
"roles.gotRole": "{role} szerepet kaptál",
"roles.lostRole": "Elvesztetted a {role} szerepet",
"tooltip.login": "Belépés",
"tooltip.logout": "Kilépés",
"tooltip.admitFromLobby": "Beengedem az előszobából",
"tooltip.admitFromLobby": "Beenegdem az előszobából",
"tooltip.lockRoom": "A konferenciába való belépés letiltása",
"tooltip.unLockRoom": "konferenciába való belépés engedélyezése",
"tooltip.enterFullscreen": "Teljes képernyős mód",
@ -81,14 +60,6 @@
"tooltip.lobby": "Az előszobában várakozók listája",
"tooltip.settings": "Beállítások",
"tooltip.participants": "Résztvevők",
"tooltip.kickParticipant": "Résztvevő kirúgása",
"tooltip.muteParticipant": "Résztvevő némítása",
"tooltip.muteParticipantVideo": "Résztvevő videóstreamének némítása",
"tooltip.raisedHand": "Jelentkezés",
"tooltip.muteScreenSharing": "Képernyőmegosztás szüneteltetése",
"tooltip.muteParticipantAudioModerator": "Résztvevő hangjának némítása mindenkinél",
"tooltip.muteParticipantVideoModerator": "Résztvevő videójának némítása mindenkinél",
"tooltip.muteScreenSharingModerator": "Résztvevő képernyőmegosztásának leállítása mindenkinél",
"label.roomName": "Konferencia",
"label.chooseRoomButton": "Tovább",
@ -102,7 +73,6 @@
"label.filesharing": "Fájl megosztás",
"label.participants": "Résztvevők",
"label.shareFile": "Fájl megosztása",
"label.shareGalleryFile": "Fájl megosztás galériából",
"label.fileSharingUnsupported": "Fájl megosztás nem támogatott",
"label.unknown": "Ismeretlen",
"label.democratic": "Egyforma képméretű képkiosztás",
@ -113,50 +83,25 @@
"label.veryHigh": "Nagyon magas (FHD)",
"label.ultra": "Ultra magas (UHD)",
"label.close": "Bezár",
"label.media": "Média",
"label.appearance": "Megjelenés",
"label.advanced": "Részletek",
"label.addVideo": "Videó hozzáadása",
"label.promoteAllPeers": "Mindenkit beengedek",
"label.moreActions": "További műveletek",
"label.version": "Verzió",
"settings.settings": "Beállítások",
"settings.camera": "Kamera",
"settings.selectCamera": "Válassz videoeszközt",
"settings.cantSelectCamera": "Nem lehet a videoeszközt kiválasztani",
"settings.selectCamera": "Válasz videóeszközt",
"settings.cantSelectCamera": "Nem lehet a videó eszközt kiválasztani",
"settings.audio": "Hang eszköz",
"settings.selectAudio": "Válassz hangeszközt",
"settings.cantSelectAudio": "Nem sikerült a hangeszközt kiválasztani",
"settings.audioOutput": "Kimenti hangeszköz",
"settings.selectAudioOutput": "Válassz kimenti hangeszközt",
"settings.cantSelectAudioOutput": "Nem sikerült a kimeneti hangeszközt kiválasztani",
"settings.resolution": "Válaszd ki a videoeszközöd felbontását",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.selectAudio": "Válasz hangeszközt",
"settings.cantSelectAudio": "Nem lehet a hang eszközt kiválasztani",
"settings.resolution": "Válaszd ki a videóeszközöd felbontását",
"settings.layout": "A konferencia képkiosztása",
"settings.selectRoomLayout": "Válaszd ki a konferencia képkiosztását",
"settings.advancedMode": "Részletes információk",
"settings.permanentTopBar": "Állandó felső sáv",
"settings.lastn": "A látható videók száma",
"settings.hiddenControls": "Média Gombok automatikus elrejtése",
"settings.notificationSounds": "Értesítések hangjelzéssel",
"settings.showNotifications": "Értesítések megjelenítése",
"settings.buttonControlBar": "Médiavezérlő gombok leválasztása",
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": "Speciális hang beállítások",
"settings.echoCancellation": "Visszhangelnyomás",
"settings.autoGainControl": "Automatikus hangerő",
"settings.noiseSuppression": "Zajelnyomás",
"settings.drawerOverlayed": "Oldalsáv a tartalom felett",
"settings.voiceActivatedUnmute": "Beszéd aktivált mikrofon némítás",
"settings.noiseThreshold": "Zajszint",
"filesharing.saveFileError": "A file-t nem sikerült elmenteni",
"filesharing.startingFileShare": "Fájl megosztása",
"filesharing.successfulFileShare": "A fájl sikeresen megosztva",
"filesharing.unableToShare": "Sikertelen fájl megosztás",
"filesharing.unableToShare": "Sikereteln fájl megosztás",
"filesharing.error": "Hiba a fájlmegosztás során",
"filesharing.finished": "A fájl letöltés befejeződött",
"filesharing.save": "Mentés",
@ -166,7 +111,7 @@
"devices.devicesChanged": "Az eszközei megváltoztak, konfiguráld őket be a beállítások menüben",
"device.audioUnsupported": "A hang nem támogatott",
"device.audioUnsupported": "A hnag nem támogatott",
"device.activateAudio": "Hang aktiválása",
"device.muteAudio": "Hang némítása",
"device.unMuteAudio": "Hang némítás kikapcsolása",
@ -177,12 +122,12 @@
"device.screenSharingUnsupported": "A képernyő megosztás nem támogatott",
"device.startScreenSharing": "Képernyőmegosztás indítása",
"device.stopScreenSharing": "Képernyőmegosztás leállítása",
"device.stopScreenSharing": "Képernyőmegosztás leáłłítása",
"devices.microphoneDisconnected": "Mikrofon kapcsolat bontva",
"devices.microphoneDisconnected": "Microphone kapcsolat bontva",
"devices.microphoneError": "Hiba történt a mikrofon hangeszköz elérése közben",
"devices.microphoneMute": "A mikrofon némítva lett",
"devices.microphoneUnMute": "A mikrofon némítása ki lett kapocsolva",
"devices.microPhoneMute": "A mikrofon némítva lett",
"devices.micophoneUnMute": "A mikrofon némítása ki lett kapocsolva",
"devices.microphoneEnable": "A mikrofon engedéylezve",
"devices.microphoneMuteError": "Nem sikerült a mikrofonod némítása",
"devices.microphoneUnMuteError": "Nem sikerült a mikrofonod némításának kikapcsolása",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Hiba történt a képernyőd megosztása során",
"devices.cameraDisconnected": "A kamera kapcsolata lebomlott",
"devices.cameraError": "Hiba történt a kamera elérése során",
"moderator.clearChat": "A moderátor kiürítette a chat történelmet",
"moderator.clearFiles": "A moderátor kiürítette a file megosztás történelmet",
"moderator.muteAudio": "A moderátor elnémította a hangod",
"moderator.muteVideo": "A moderátor elnémította a videód",
"moderator.stopScreenSharing": "A moderátor leállította a képernyőmegosztásod",
"unsupportedBrowser.titleUnsupportedBrowser": "A bőngésző verziód sajnos nem támogatott! :-(",
"unsupportedBrowser.titlewebrtcUnavailable": "A böngésződ egy szükséges funkciója nem elérhető!",
"unsupportedBrowser.bodyText": "Kérlek frissítsd a böngésződ, válts másik böngészőre, vagy ellenőrizd a böngésződ beállításait! Támogatott böngészők:"
"devices.cameraError": "Hiba történt a kamera elérése során"
}

View File

@ -49,31 +49,10 @@
"room.spotlights": "Partecipanti in Evidenza",
"room.passive": "Participanti Passivi",
"room.videoPaused": "Il video è in pausa",
"room.muteAll": "Muta tutti",
"room.stopAllVideo": "Ferma tutti i video",
"room.closeMeeting": "Termina meeting",
"room.clearChat": "Pulisci chat",
"room.clearFileSharing": "Pulisci file sharing",
"room.speechUnsupported": "Il tuo browser non supporta il riconoscimento vocale",
"room.moderatoractions": "Azioni moderatore",
"room.raisedHand": "{displayName} ha alzato la mano",
"room.loweredHand": "{displayName} ha abbassato la mano",
"room.extraVideo": "Video extra",
"room.overRoomLimit": "La stanza è piena, riprova più tardi.",
"room.help": "Aiuto",
"room.about": "Informazioni su",
"room.shortcutKeys": "Scorciatoie da tastiera",
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": "Sei mutato, tieni premuto SPAZIO per parlare",
"roles.gotRole": "Hai ottenuto il ruolo: {role}",
"roles.lostRole": "Hai perso il ruolo: {role}",
"tooltip.login": "Log in",
"tooltip.logout": "Log out",
"tooltip.admitFromLobby": "Accetta partecipante dalla lobby",
"tooltip.admitFromLobby": "Ammetti dalla lobby",
"tooltip.lockRoom": "Blocca stanza",
"tooltip.unLockRoom": "Sblocca stanza",
"tooltip.enterFullscreen": "Modalità schermo intero",
@ -81,14 +60,6 @@
"tooltip.lobby": "Mostra lobby",
"tooltip.settings": "Mostra impostazioni",
"tooltip.participants": "Mostra partecipanti",
"tooltip.kickParticipant": "Espelli partecipante",
"tooltip.muteParticipant": "Muta partecipante",
"tooltip.muteParticipantVideo": "Ferma video partecipante",
"tooltip.raisedHand": "Mano alzata",
"tooltip.muteScreenSharing": "Ferma condivisione schermo partecipante",
"tooltip.muteParticipantAudioModerator": "Sospendi audio globale",
"tooltip.muteParticipantVideoModerator": "Sospendi video globale",
"tooltip.muteScreenSharingModerator": "Sospendi condivisione schermo globale",
"label.roomName": "Nome della stanza",
"label.chooseRoomButton": "Continua",
@ -102,7 +73,6 @@
"label.filesharing": "Condivisione file",
"label.participants": "Partecipanti",
"label.shareFile": "Condividi file",
"label.shareGalleryFile": "Condividi immagine",
"label.fileSharingUnsupported": "Condivisione file non supportata",
"label.unknown": "Sconosciuto",
"label.democratic": "Vista Democratica",
@ -113,13 +83,6 @@
"label.veryHigh": "Molto alta (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Chiudi",
"label.media": "Media",
"label.appearance": "Aspetto",
"label.advanced": "Avanzate",
"label.addVideo": "Aggiungi video",
"label.promoteAllPeers": "Promuovi tutti",
"label.moreActions": "Altre azioni",
"label.version": null,
"settings.settings": "Impostazioni",
"settings.camera": "Videocamera",
@ -128,30 +91,12 @@
"settings.audio": "Dispositivo audio",
"settings.selectAudio": "Seleziona dispositivo audio",
"settings.cantSelectAudio": "Impossibile selezionare dispositivo audio",
"settings.audioOutput": "Dispositivo di uscita audio",
"settings.selectAudioOutput": "Seleziona il dispositivo di uscita audio",
"settings.cantSelectAudioOutput": "Impossibile selezionare il dispositivo di uscita audio",
"settings.resolution": "Seleziona risoluzione",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Aspetto stanza",
"settings.selectRoomLayout": "Seleziona aspetto stanza",
"settings.advancedMode": "Modalità avanzata",
"settings.permanentTopBar": "Barra superiore permanente",
"settings.lastn": "Numero di video visibili",
"settings.hiddenControls": "Controlli media nascosti",
"settings.notificationSounds": "Suoni di notifica",
"settings.showNotifications": "Mostra notifiche",
"settings.buttonControlBar": "Controlli media separati",
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": "Cancellazione echo",
"settings.autoGainControl": "Controllo guadagno automatico",
"settings.noiseSuppression": "Riduzione del rumore",
"settings.drawerOverlayed": "Barra laterale sovrapposta",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Impossibile salvare file",
"filesharing.startingFileShare": "Tentativo di condivisione file",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Microfono scollegato",
"devices.microphoneError": "Errore con l'accesso al microfono",
"devices.microphoneMute": "Microfono silenziato",
"devices.microphoneUnMute": "Microfono riattivato",
"devices.microPhoneMute": "Microfono silenziato",
"devices.micophoneUnMute": "Microfono riattivato",
"devices.microphoneEnable": "Microfono attivo",
"devices.microphoneMuteError": "Impossibile silenziare il microfono",
"devices.microphoneUnMuteError": "Impossibile riattivare il microfono",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Errore con l'accesso al tuo schermo",
"devices.cameraDisconnected": "Videocamera scollegata",
"devices.cameraError": "Errore con l'accesso alla videocamera",
"moderator.clearChat": "Il moderatore ha pulito la chat",
"moderator.clearFiles": "Il moderatore ha pulito i file",
"moderator.muteAudio": "Il moderatore ha mutato il tuo audio",
"moderator.muteVideo": "Il moderatore ha fermato il tuo video",
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Errore con l'accesso alla videocamera"
}

View File

@ -1,199 +0,0 @@
{
"socket.disconnected": "Esat bezsaistē",
"socket.reconnecting": "Esat bezsaistē, tiek mēģināts pievienoties",
"socket.reconnected": "Esat atkārtoti pievienojies",
"socket.requestError": "Kļūme servera pieprasījumā",
"room.chooseRoom": "Ievadiet sapulces telpas nosaukumu (ID), kurai vēlaties pievienoties",
"room.cookieConsent": "Lai uzlabotu lietotāja pieredzi, šī vietne izmanto sīkfailus",
"room.consentUnderstand": "Es saprotu un piekrītu",
"room.joined": "Jūs esiet pievienojies sapulces telpai",
"room.cantJoin": "Nav iespējams pievienoties sapulces telpai",
"room.youLocked": "Jūs aizslēdzāt sapulces telpu",
"room.cantLock": "Nav iespējams aizslēgt sapulces telpu",
"room.youUnLocked": "Jūs atslēdzāt sapulces telpu",
"room.cantUnLock": "Nav iespējams atslēgt sapulces telpu",
"room.locked": "Sapulces telpa tagad ir AIZSLĒGTA",
"room.unlocked": "Sapulces telpa tagad ir ATSLĒGTA",
"room.newLobbyPeer": "Jauns dalībnieks ienācis uzgaidāmajā telpā",
"room.lobbyPeerLeft": "Dalībnieks uzgaidāmo telpu pameta",
"room.lobbyPeerChangedDisplayName": "Dalībnieks uzgaidāmajā telpā nomainīja vārdu uz {displayName}",
"room.lobbyPeerChangedPicture": "Dalībnieks uzgaidāmajā telpā nomainīja pašattēlu",
"room.setAccessCode": "Pieejas kods sapulces telpai aktualizēts",
"room.accessCodeOn": "Pieejas kods sapulces telpai tagad ir aktivēts",
"room.accessCodeOff": "Pieejas kods sapulces telpai tagad ir deaktivēts (atslēgts)",
"room.peerChangedDisplayName": "{oldDisplayName} pārsaucās par {displayName}",
"room.newPeer": "{displayName} pievienojās sapulces telpai",
"room.newFile": "Pieejams jauns fails",
"room.toggleAdvancedMode": "Pārslēgt uz advancēto režīmu",
"room.setDemocraticView": "Nomainīts izkārtojums uz demokrātisko skatu",
"room.setFilmStripView": "Nomainīts izkārtojums uz diapozitīvu (filmstrip) skatu",
"room.loggedIn": "Jūs esat ierakstījies (sistēmā)",
"room.loggedOut": "Jūs esat izrakstījies (no sistēmas)",
"room.changedDisplayName": "Jūsu vārds mainīts uz {displayName}",
"room.changeDisplayNameError": "Gadījās ķibele ar Jūsu vārda nomaiņu",
"room.chatError": "Nav iespējams nosūtīt tērziņa ziņu",
"room.aboutToJoin": "Jūs grasāties pievienoties sapulcei",
"room.roomId": "Sapulces telpas nosaukums (ID): {roomName}",
"room.setYourName": "Norādiet savu dalības vārdu un izvēlieties kā vēlaties pievienoties sapulcei:",
"room.audioOnly": "Vienīgi audio",
"room.audioVideo": "Audio & video",
"room.youAreReady": "Ok, Jūs esiet gatavi!",
"room.emptyRequireLogin": "Sapulces telpa ir tukša! Jūs varat Ierakstīties sistēmā, lai uzsāktu vadīt sapulci vai pagaidīt kamēr pievienojas sapulces rīkotājs/vadītājs",
"room.locketWait": "Sapulce telpa ir slēgta. Jūs atrodaties tās uzgaidāmajā telpā. Uzkavējieties, kamēr kāds Jūs sapulcē ielaiž ...",
"room.lobbyAdministration": "Uzgaidāmās telpas administrēšana",
"room.peersInLobby": "Dalībnieki uzgaidāmajā telpā",
"room.lobbyEmpty": "Pašreiz uzgaidāmajā telpā neviena nav",
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
"room.me": "Es",
"room.spotlights": "Aktīvie (referējošie) dalībnieki",
"room.passive": "Pasīvie dalībnieki",
"room.videoPaused": "Šis video ir pauzēts",
"room.muteAll": "Noklusināt visus dalībnieku mikrofonus",
"room.stopAllVideo": "Izslēgt visu dalībnieku kameras",
"room.closeMeeting": "Beigt sapulci",
"room.clearChat": "Nodzēst visus tērziņus",
"room.clearFileSharing": "Notīrīt visus kopīgotos failus",
"room.speechUnsupported": "Jūsu pārlūks neatbalsta balss atpazīšanu",
"room.moderatoractions": "Moderatora rīcība",
"room.raisedHand": "{displayName} pacēla roku",
"room.loweredHand": "{displayName} nolaida roku",
"room.extraVideo": "Papildus video",
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": "Jūs esat noklusināts. Turiet taustiņu SPACE-BAR, lai runātu",
"roles.gotRole": "Jūs ieguvāt lomu: {role}",
"roles.lostRole": "Jūs zaudējāt lomu: {role}",
"tooltip.login": "Ierakstīties",
"tooltip.logout": "Izrakstīties",
"tooltip.admitFromLobby": "Ielaist no uzgaidāmās telpas",
"tooltip.lockRoom": "Aizslēgt sapulces telpu",
"tooltip.unLockRoom": "Atlēgt sapulces telpu",
"tooltip.enterFullscreen": "Aktivēt pilnekrāna režīmu",
"tooltip.leaveFullscreen": "Pamest pilnekrānu",
"tooltip.lobby": "Parādīt uzgaidāmo telpu",
"tooltip.settings": "Parādīt iestatījumus",
"tooltip.participants": "Parādīt dalībniekus",
"tooltip.kickParticipant": "Izvadīt (izspert) dalībnieku",
"tooltip.muteParticipant": "Noklusināt dalībnieku",
"tooltip.muteParticipantVideo": "Atslēgt dalībnieka video",
"tooltip.raisedHand": "Pacelt roku",
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Sapulces telpas nosaukums (ID)",
"label.chooseRoomButton": "Turpināt",
"label.yourName": "Jūu vārds",
"label.newWindow": "Jauns logs",
"label.fullscreen": "Pilnekrāns",
"label.openDrawer": "Atvērt atvilkni",
"label.leave": "Pamest",
"label.chatInput": "Rakstiet tērziņa ziņu...",
"label.chat": "Tērzētava",
"label.filesharing": "Failu koplietošana",
"label.participants": "Dalībnieki",
"label.shareFile": "Koplietot failu",
"label.fileSharingUnsupported": "Failu koplietošana netiek atbalstīta",
"label.unknown": "Nezināms",
"label.democratic": "Demokrātisks skats",
"label.filmstrip": "Diapozitīvu (filmstrip) skats",
"label.low": "Zema",
"label.medium": "Vidēja",
"label.high": "Augsta (HD)",
"label.veryHigh": "Ļoti augsta (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Aizvērt",
"label.media": "Mediji",
"label.appearance": "Izskats",
"label.advanced": "Advancēts",
"label.addVideo": "Pievienot video",
"label.moreActions": null,
"label.version": null,
"settings.settings": "Iestatījumi",
"settings.camera": "Kamera",
"settings.selectCamera": "Izvēlieties kameru (video ierīci)",
"settings.cantSelectCamera": "Nav iespējams lietot šo kameru (video ierīci)",
"settings.audio": "Skaņas ierīce",
"settings.selectAudio": "Izvēlieties skaņas ierīci",
"settings.cantSelectAudio": "Nav iespējams lietot šo skaņas (audio) ierīci",
"settings.resolution": "Iestatiet jūsu video izšķirtspēju",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Sapulces telpas izkārtojums",
"settings.selectRoomLayout": "Iestatiet sapulces telpas izkārtojumu",
"settings.advancedMode": "Advancētais režīms",
"settings.permanentTopBar": "Pastāvīga augšējā (ekrānaugšas) josla",
"settings.lastn": "Jums redzamo video/kameru skaits",
"settings.hiddenControls": "Slēpto mediju vadība",
"settings.notificationSounds": "Paziņojumu skaņas",
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Nav iespējams saglabāt failu",
"filesharing.startingFileShare": "Tiek mēģināts kopīgot failu",
"filesharing.successfulFileShare": "Fails sekmīgi kopīgots",
"filesharing.unableToShare": "Nav iespējams kopīgot failu",
"filesharing.error": "Atgadījās faila kopīgošanas kļūme",
"filesharing.finished": "Fails ir lejupielādēts",
"filesharing.save": "Saglabāt",
"filesharing.sharedFile": "{displayName} kopīgoja failu",
"filesharing.download": "Lejuplādēt",
"filesharing.missingSeeds": "Ja šis process aizņem ilgu laiku, iespējams nav neviena, kas sēklo (seed) šo torentu. Mēģiniet palūgt kādu atkārtoti augšuplādēt Jūsu gribēto failu.",
"devices.devicesChanged": "Jūsu ierīces pamainījās. Iestatījumu izvēlnē (dialogā) iestatiet jaunās ierīces.",
"device.audioUnsupported": "Skaņa (audio) netiek atbalstīta",
"device.activateAudio": "Iespējot/aktivēt mikrofonu (izejošo skaņu)",
"device.muteAudio": "Atslēgt/noklusināt mikrofonu (izejošo skaņu) ",
"device.unMuteAudio": "Ieslēgt mikrofonu (izejošo skaņu)",
"device.videoUnsupported": "Kamera (izejošais video) netiek atbalstīta",
"device.startVideo": "Ieslēgt kameru (izejošo video)",
"device.stopVideo": "Izslēgt kameru (izejošo video)",
"device.screenSharingUnsupported": "Ekrāna kopīgošana netiek atbalstīta",
"device.startScreenSharing": "Sākt ekrāna kopīgošanu",
"device.stopScreenSharing": "Beigt ekrāna kopīgošanu",
"devices.microphoneDisconnected": "Mikrofons atvienots",
"devices.microphoneError": "Atgadījās kļūme, piekļūstot jūsu mikrofonam",
"devices.microPhoneMute": "Mikrofons izslēgts/noklusināts",
"devices.micophoneUnMute": "Mikrofons ieslēgts",
"devices.microphoneEnable": "Mikrofons iespējots",
"devices.microphoneMuteError": "Nav iespējams izslēgt Jūsu mikrofonu",
"devices.microphoneUnMuteError": "Nav iespējams ieslēgt Jūsu mikrofonu",
"devices.screenSharingDisconnected" : "Ekrāna kopīgošana nenotiek (atvienota)",
"devices.screenSharingError": "Atgadījās kļūme, piekļūstot Jūsu ekrānam",
"devices.cameraDisconnected": "Kamera atvienota",
"devices.cameraError": "Atgadījās kļūme, piekļūstot Jūsu kamerai",
"moderator.clearChat": "Moderators nodzēsa tērziņus",
"moderator.clearFiles": "Moderators notīrīja failus",
"moderator.muteAudio": "Moderators noklusināja jūsu mikrofonu",
"moderator.muteVideo": "Moderators atslēdza jūsu kameru",
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Deltakere i fokus",
"room.passive": "Passive deltakere",
"room.videoPaused": "Denne videoen er inaktiv",
"room.muteAll": "Demp alle",
"room.stopAllVideo": "Stopp all video",
"room.closeMeeting": "Avslutt møte",
"room.clearChat": "Tøm chat",
"room.clearFileSharing": "Fjern filer",
"room.speechUnsupported": "Din nettleser støtter ikke stemmegjenkjenning",
"room.moderatoractions": "Moderatorhandlinger",
"room.raisedHand": "{displayName} rakk opp hånden",
"room.loweredHand": "{displayName} tok ned hånden",
"room.extraVideo": "Ekstra video",
"room.overRoomLimit": "Rommet er fullt, prøv igjen om litt.",
"room.help": "Hjelp",
"room.about": "Om",
"room.shortcutKeys": "Hurtigtaster",
"room.browsePeersSpotlight": "Bytt mellom synlig deltaker",
"room.stopAllScreenSharing": "Stopp all skjermdeling",
"me.mutedPTT": "Du er dempet, hold nede SPACE for å snakke",
"roles.gotRole": "Du fikk rollen: {role}",
"roles.lostRole": "Du mistet rollen: {role}",
"tooltip.login": "Logg in",
"tooltip.logout": "Logg ut",
@ -81,14 +60,6 @@
"tooltip.lobby": "Vis lobby",
"tooltip.settings": "Vis innstillinger",
"tooltip.participants": "Vis deltakere",
"tooltip.kickParticipant": "Spark ut deltaker",
"tooltip.muteParticipant": "Demp deltaker",
"tooltip.muteParticipantVideo": "Demp deltakervideo",
"tooltip.raisedHand": "Rekk opp hånden",
"tooltip.muteScreenSharing": "Demp deltaker skjermdeling",
"tooltip.muteParticipantAudioModerator": "Demp deltaker globalt",
"tooltip.muteParticipantVideoModerator": "Demp deltakervideo globalt",
"tooltip.muteScreenSharingModerator": "Demp deltakerskjermdeling globalt",
"label.roomName": "Møtenavn",
"label.chooseRoomButton": "Fortsett",
@ -102,7 +73,6 @@
"label.filesharing": "Fildeling",
"label.participants": "Deltakere",
"label.shareFile": "Del fil",
"label.shareGalleryFile": "Del bilde",
"label.fileSharingUnsupported": "Fildeling ikke støttet",
"label.unknown": "Ukjent",
"label.democratic": "Demokratisk",
@ -113,13 +83,6 @@
"label.veryHigh": "Veldig høy (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Lukk",
"label.media": "Media",
"label.appearance": "Utseende",
"label.advanced": "Avansert",
"label.addVideo": "Legg til video",
"label.promoteAllPeers": "Slipp inn alle",
"label.moreActions": "Flere handlinger",
"label.version": "Versjon",
"settings.settings": "Innstillinger",
"settings.camera": "Kamera",
@ -128,30 +91,12 @@
"settings.audio": "Lydenhet",
"settings.selectAudio": "Velg lydenhet",
"settings.cantSelectAudio": "Kan ikke velge lydenhet",
"settings.audioOutput": "Lydutgangsenhet",
"settings.selectAudioOutput": "Velg lydutgangsenhet",
"settings.cantSelectAudioOutput": "Kan ikke velge lydutgangsenhet",
"settings.resolution": "Velg oppløsning",
"settings.frameRate": "Velg bildefrekvens",
"settings.screenSharingResolution": "Velg skjermdelingsoppløsning",
"settings.screenSharingFrameRate": "Velg skjermdelingsbildefrekvens",
"settings.layout": "Møtelayout",
"settings.selectRoomLayout": "Velg møtelayout",
"settings.advancedMode": "Avansert modus",
"settings.permanentTopBar": "Permanent topplinje",
"settings.lastn": "Antall videoer synlig",
"settings.showAdvancedVideo": "Avanserte videoinnstillinger",
"settings.showAdvancedAudio": "Avanserte audioinnstillinger",
"settings.hiddenControls": "Skjul media knapper",
"settings.notificationSounds": "Varslingslyder",
"settings.showNotifications": "Vis varslinger",
"settings.buttonControlBar": "Separate media knapper",
"settings.echoCancellation": "Echokansellering",
"settings.autoGainControl": "Auto gain kontroll",
"settings.noiseSuppression": "Støy reduksjon",
"settings.drawerOverlayed": "Sidemeny over innhold",
"settings.voiceActivatedUnmute": "Stemmeaktivert autodemping",
"settings.noiseThreshold": "Støyterskel",
"filesharing.saveFileError": "Klarte ikke å lagre fil",
"filesharing.startingFileShare": "Starter fildeling",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Mikrofon koblet fra",
"devices.microphoneError": "Det skjedde noe feil med mikrofonen din",
"devices.microphoneMute": "Dempet mikrofonen",
"devices.microphoneUnMute": "Aktiverte mikrofonen",
"devices.microPhoneMute": "Dempet mikrofonen",
"devices.micophoneUnMute": "Aktiverte mikrofonen",
"devices.microphoneEnable": "Aktiverte mikrofonen",
"devices.microphoneMuteError": "Klarte ikke å dempe mikrofonen",
"devices.microphoneUnMuteError": "Klarte ikke å aktivere mikrofonen",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Det skjedde noe feil med skjermdelingen din",
"devices.cameraDisconnected": "Kamera koblet fra",
"devices.cameraError": "Det skjedde noe feil med kameraet ditt",
"moderator.clearChat": "Moderator tømte chatten",
"moderator.clearFiles": "Moderator fjernet filer",
"moderator.muteAudio": "Moderator mutet lyden din",
"moderator.muteVideo": "Moderator mutet videoen din",
"moderator.stopScreenSharing": "Moderator mutet skjermdelingen din",
"unsupportedBrowser.titleUnsupportedBrowser": "Nettleser ikke støttet",
"unsupportedBrowser.titlewebrtcUnavailable": "Funksjonalitet som kreves er ikke tilgjengelig i denne nettleseren",
"unsupportedBrowser.bodyText": "Denne møtetjenesten krever funksjonalitet som ikke er støttet i denne nettleseren. Vennligst oppgrader, bytt til en annen nettleser, eller sjekk innstillinger. Støttede nettlesere:"
"devices.cameraError": "Det skjedde noe feil med kameraet ditt"
}

View File

@ -6,7 +6,7 @@
"room.chooseRoom": "Wybór konferencji",
"room.cookieConsent": "Ta strona internetowa wykorzystuje pliki cookie w celu zwiększenia wygody użytkowania.",
"room.consentUnderstand": "Rozumiem",
"room.consentUnderstand": "I understand",
"room.joined": "Podłączono do konferencji",
"room.cantJoin": "Brak możliwości dołączenia do pokoju",
"room.youLocked": "Zakluczono pokój",
@ -49,27 +49,6 @@
"room.spotlights": "Aktywni uczestnicy",
"room.passive": "Pasywni uczestnicy",
"room.videoPaused": "To wideo jest wstrzymane.",
"room.muteAll": "Wycisz wszystkich",
"room.stopAllVideo": "Zatrzymaj wszystkie Video",
"room.closeMeeting": "Zamknij spotkanie",
"room.clearChat": "Wyczyść Chat",
"room.clearFileSharing": "Wyczyść pliki",
"room.speechUnsupported": "Twoja przeglądarka nie rozpoznaje mowy",
"room.moderatoractions": "Akcje moderatora",
"room.raisedHand": "{displayName} podniósł rękę",
"room.loweredHand": "{displayName} opuścił rękę",
"room.extraVideo": "Dodatkowe Video",
"room.overRoomLimit": "Pokój jest pełny, spróbuj za jakiś czas.",
"room.help": "Pomoc",
"room.about": "O pogramie",
"room.shortcutKeys": "Skróty klawiaturowe",
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": "Masz wyciszony mikrofon, przytrzymaj spację aby mówić",
"roles.gotRole": "Masz rolę {role}",
"roles.lostRole": "Nie masz już roli {role}",
"tooltip.login": "Zaloguj",
"tooltip.logout": "Wyloguj",
@ -81,14 +60,6 @@
"tooltip.lobby": "Pokaż poczekalnię",
"tooltip.settings": "Pokaż ustawienia",
"tooltip.participants": "Pokaż uczestników",
"tooltip.kickParticipant": "Wyrzuć użytkownika",
"tooltip.muteParticipant": "Wycisz użytkownika",
"tooltip.muteParticipantVideo": "Wyłącz wideo użytkownika",
"tooltip.raisedHand": "Podnieś rękę",
"tooltip.muteScreenSharing": "Anuluj udostępniania pulpitu przez użytkownika",
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Nazwa konferencji",
"label.chooseRoomButton": "Kontynuuj",
@ -102,7 +73,6 @@
"label.filesharing": "Udostępnianie plików",
"label.participants": "Uczestnicy",
"label.shareFile": "Udostępnij plik",
"label.shareGalleryFile": "Udostępnij obraz",
"label.fileSharingUnsupported": "Udostępnianie plików nie jest obsługiwane",
"label.unknown": "Nieznane",
"label.democratic": "Układ demokratyczny",
@ -113,13 +83,6 @@
"label.veryHigh": "Bardzo wysoka (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Zamknij",
"label.media": "Media",
"label.appearance": "Wygląd",
"label.advanced": "Zaawansowane",
"label.addVideo": "Dodaj wideo",
"label.promoteAllPeers": "Wpuść wszystkich",
"label.moreActions": "Więcej akcji",
"label.version": null,
"settings.settings": "Ustawienia",
"settings.camera": "Kamera",
@ -128,30 +91,12 @@
"settings.audio": "Urządzenie audio",
"settings.selectAudio": "Wybór urządzenia audio",
"settings.cantSelectAudio": "Nie można wybrać urządzenia audio",
"settings.audioOutput": "Urządzenie wyjściowe audio",
"settings.selectAudioOutput": "Wybierz urządzenie wyjściowe audio",
"settings.cantSelectAudioOutput": "Nie można wybrać urządzenia wyjściowego audio",
"settings.resolution": "Wybór rozdzielczości wideo",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Układ konferencji",
"settings.selectRoomLayout": "Ustawienia układu konferencji",
"settings.advancedMode": "Tryb zaawansowany",
"settings.permanentTopBar": "Stały górny pasek",
"settings.lastn": "Liczba widocznych uczestników (zdalnych)",
"settings.hiddenControls": "Ukryte kontrolki mediów",
"settings.notificationSounds": "Powiadomienia dźwiękiem",
"settings.showNotifications": "Pokaż powiadomienia",
"settings.buttonControlBar": "Rozdziel kontrolki mediów",
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": "Usuwanie echa",
"settings.autoGainControl": "Auto korekta wzmocnienia",
"settings.noiseSuppression": "Wyciszenie szumów",
"settings.drawerOverlayed": "Szuflada nad zawartością",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"settings.lastn": "Liczba widocznych filmów",
"filesharing.saveFileError": "Nie można zapisać pliku",
"filesharing.startingFileShare": "Próba udostępnienia pliku",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Odłączono mikrofon",
"devices.microphoneError": "Błąd dostępu do mikrofonu",
"devices.microphoneMute": "Wyciszenie mikrofonu włączone",
"devices.microphoneUnMute": "Wyciszenie mikrofonu wyłączone",
"devices.microPhoneMute": "Wyciszenie mikrofonu włączone",
"devices.micophoneUnMute": "Wyciszenie mikrofonu wyłączone",
"devices.microphoneEnable": "Włączono mikrofon",
"devices.microphoneMuteError": "Nie można wyciszyć mikrofonu",
"devices.microphoneUnMuteError": "Nie można wyłączyć wyciszenia mikrofonu.",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Wystąpił błąd podczas uzyskiwania dostępu do ekranu",
"devices.cameraDisconnected": "Kamera odłączona",
"devices.cameraError": "Wystąpił błąd podczas uzyskiwania dostępu do kamery",
"moderator.clearChat": "Moderator wyczyścił chat",
"moderator.clearFiles": "Moderator wyczyścił pliki",
"moderator.muteAudio": "Moderator wyciszył audio",
"moderator.muteVideo": "Moderator wyciszył twoje video",
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Wystąpił błąd podczas uzyskiwania dostępu do kamery"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Participantes em foco",
"room.passive": "Participantes passivos",
"room.videoPaused": "Este vídeo está em pausa",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Entrar",
"tooltip.logout": "Sair",
@ -81,14 +60,6 @@
"tooltip.lobby": "Apresentar sala de espera",
"tooltip.settings": "Apresentar definições",
"tooltip.participants": "Apresentar participantes",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Nome da sala",
"label.chooseRoomButton": "Continuar",
@ -102,7 +73,6 @@
"label.filesharing": "Partilha de ficheiro",
"label.participants": "Participantes",
"label.shareFile": "Partilhar ficheiro",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Partilha de ficheiro não disponível",
"label.unknown": "Desconhecido",
"label.democratic": "Vista democrática",
@ -113,13 +83,6 @@
"label.veryHigh": "Muito alta (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Fechar",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Definições",
"settings.camera": "Camera",
@ -128,30 +91,12 @@
"settings.audio": "Dispositivo Áudio",
"settings.selectAudio": "Selecione o seu dispositivo de áudio",
"settings.cantSelectAudio": "Impossível selecionar o seu dispositivo de áudio",
"settings.audioOutput": "Dispositivo de saída de áudio",
"settings.selectAudioOutput": "Selecionar dispositivo de saída de áudio",
"settings.cantSelectAudioOutput": "Não foi possível selecionar o dispositivo de saída de áudio",
"settings.resolution": "Selecione a sua resolução de vídeo",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Disposição da sala",
"settings.selectRoomLayout": "Seleccione a disposição da sala",
"settings.advancedMode": "Modo avançado",
"settings.permanentTopBar": "Barra superior permanente",
"settings.lastn": "Número de vídeos visíveis",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Impossível de gravar o ficheiro",
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
@ -181,8 +126,8 @@
"devices.microphoneDisconnected": "Microfone desiligado",
"devices.microphoneError": "Ocorreu um erro no acesso ao microfone",
"devices.microphoneMute": "Som microfone desativado",
"devices.microphoneUnMute": "Som mmicrofone ativado",
"devices.microPhoneMute": "Som microfone desativado",
"devices.micophoneUnMute": "Som mmicrofone ativado",
"devices.microphoneEnable": "Microfone ativado",
"devices.microphoneMuteError": "Não foi possível cortar o som do microfone",
"devices.microphoneUnMuteError": "Não foi possível ativar o som do microfone",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Ocorreu um erro no acesso ao seu ecrã",
"devices.cameraDisconnected": "Câmara desconectada",
"devices.cameraError": "Ocorreu um erro no acesso à sua câmara",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Ocorreu um erro no acesso à sua câmara"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Participanți în Spotlight",
"room.passive": "Participanți pasivi",
"room.videoPaused": "Acest video este pus pe pauză",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Intră în cont",
"tooltip.logout": "Deconectare",
@ -79,16 +58,7 @@
"tooltip.enterFullscreen": "Modul ecran complet",
"tooltip.leaveFullscreen": "Ieșire din modul ecran complet",
"tooltip.lobby": "Arată holul",
"tooltip.settings": "Arată setăile",
"tooltip.participants": null,
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"tooltip.settings": "Arată participanții",
"label.roomName": "Numele camerei",
"label.chooseRoomButton": "Continuare",
@ -102,7 +72,6 @@
"label.filesharing": "Partajarea fișierelor",
"label.participants": "Participanți",
"label.shareFile": "Partajează fișierul",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Partajarea fișierelor nu este acceptată",
"label.unknown": "Necunoscut",
"label.democratic": "Distribuție egală a dimensiunii imaginii",
@ -113,13 +82,6 @@
"label.veryHigh": "Rezoluție foarte înaltă (FHD)",
"label.ultra": "Rezoluție ultra înaltă (UHD)",
"label.close": "Închide",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Setări",
"settings.camera": "Cameră video",
@ -128,30 +90,12 @@
"settings.audio": "Dispozitivul audio",
"settings.selectAudio": "Selectarea dispozitivul audio",
"settings.cantSelectAudio": "Încercarea de a selecta dispozitivul audio a eșuat",
"settings.audioOutput": "Dispozitiv de ieșire audio",
"settings.selectAudioOutput": "Selectați dispozitivul de ieșire audio",
"settings.cantSelectAudioOutput": "Imposibil de selectat dispozitivul de ieșire audio",
"settings.resolution": "Selectează rezoluția video",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Aspectul camerei video",
"settings.selectRoomLayout": "Selectează spectul camerei video",
"settings.advancedMode": "Mod avansat",
"settings.permanentTopBar": "Bara de sus permanentă",
"settings.lastn": "Numărul de videoclipuri vizibile",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Încercarea de a salva fișierul a eșuat",
"filesharing.startingFileShare": "Partajarea fișierului",
@ -181,8 +125,8 @@
"devices.microphoneDisconnected": "Microfonul e deconectat",
"devices.microphoneError": "A apărut o eroare la accesarea microfonului",
"devices.microphoneMute": "Microfonul e dezactivat",
"devices.microphoneUnMute": "Retragerea dezactivării microfonului",
"devices.microPhoneMute": "Microfonul e dezactivat",
"devices.micophoneUnMute": "Retragerea dezactivării microfonului",
"devices.microphoneEnable": "Microfonul e activat",
"devices.microphoneMuteError": "Încercarea de a dezactiva microfonului a eșuat",
"devices.microphoneUnMuteError": "Încercarea de a retrage dezactivarea microfonului a eșuat",
@ -191,15 +135,5 @@
"devices.screenSharingError": "A apărut o eroare la accesarea ecranului",
"devices.cameraDisconnected": "Camera video e disconectată",
"devices.cameraError": "A apărut o eroare la accesarea camerei video",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "A apărut o eroare la accesarea camerei video"
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Gündemdeki Katılımcılar",
"room.passive": "Pasif Katılımcılar",
"room.videoPaused": "Video duraklatıldı",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Giriş",
"tooltip.logout": ıkış",
@ -81,14 +60,6 @@
"tooltip.lobby": "Lobiyi göster",
"tooltip.settings": "Ayarları göster",
"tooltip.participants": "Katılımcıları göster",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Oda adı",
"label.chooseRoomButton": "Devam",
@ -102,7 +73,6 @@
"label.filesharing": "Dosya paylaşım",
"label.participants": "Katılımcı",
"label.shareFile": "Dosya paylaş",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Dosya paylaşımı desteklenmiyor",
"label.unknown": "Bilinmeyen",
"label.democratic": "Demokratik görünüm",
@ -113,13 +83,6 @@
"label.veryHigh": "Çok Yüksek (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Kapat",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Ayarlar",
"settings.camera": "Kamera",
@ -129,26 +92,11 @@
"settings.selectAudio": "Ses aygıtını seç",
"settings.cantSelectAudio": "Ses aygıtı seçilemiyor",
"settings.resolution": "Video çözünürlüğü ayarla",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Oda düzeni",
"settings.selectRoomLayout": "Oda düzeni seç",
"settings.advancedMode": "Detaylı mod",
"settings.permanentTopBar": "Üst barı kalıcı yap",
"settings.lastn": "İzlenebilir video sayısı",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Dosya kaydedilemiyor",
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
@ -188,15 +136,5 @@
"devices.screenSharingError": "Ekranınıza erişilirken bir hata oluştu",
"devices.cameraDisconnected": "Kamera bağlı değil",
"devices.cameraError": "Kameranıza erişilirken bir hata oluştu",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Kameranıza erişilirken bir hata oluştu"
}

View File

@ -1,204 +0,0 @@
{
"socket.disconnected": "您已斷開連接",
"socket.reconnecting": "嘗試重新連接",
"socket.reconnected": "您已重新連接",
"socket.requestError": "服務器請求錯誤",
"room.chooseRoom": "選擇您要加入的房間的名稱",
"room.cookieConsent": "這個網站使用Cookies來提升您的使用者體驗",
"room.consentUnderstand": "了解",
"room.joined": "您已加入房間",
"room.cantJoin": "無法加入房間",
"room.youLocked": "您已鎖定房間",
"room.cantLock": "無法鎖定房間",
"room.youUnLocked": "您解鎖了房間",
"room.cantUnLock": "無法解鎖房間",
"room.locked": "房間已鎖定",
"room.unlocked": "房間現已解鎖",
"room.newLobbyPeer": "新參與者進入大廳",
"room.lobbyPeerLeft": "參與者離開大廳",
"room.lobbyPeerChangedDisplayName": "大廳的參與者將名稱變更為 {displayName}",
"room.lobbyPeerChangedPicture": "大廳的參與者變更了圖片",
"room.setAccessCode": "設置房間的進入密碼",
"room.accessCodeOn": "房間的進入密碼現已啟用",
"room.accessCodeOff": "房間的進入密碼已停用",
"room.peerChangedDisplayName": "{oldDisplayName} 已變更名稱為 {displayName}",
"room.newPeer": "{displayName} 加入了會議室",
"room.newFile": "有新文件",
"room.toggleAdvancedMode": "切換進階模式",
"room.setDemocraticView": "已更改為使用者佈局",
"room.setFilmStripView": "已更改為投影片佈局",
"room.loggedIn": "您已登入",
"room.loggedOut": "您已登出",
"room.changedDisplayName": "您的顯示名稱已變更為 {displayName}",
"room.changeDisplayNameError": "更改顯示名稱時發生錯誤",
"room.chatError": "無法發送聊天消息",
"room.aboutToJoin": "您即將參加會議",
"room.roomId": "房間ID: {roomName}",
"room.setYourName": "設置您的顯示名稱,並選擇您想加入的方式:",
"room.audioOnly": "僅通話",
"room.audioVideo": "通話和視訊",
"room.youAreReady": "準備完畢!",
"room.emptyRequireLogin": "房間是空的! 您可以登錄以開始會議或等待主持人加入",
"room.locketWait": "房間已鎖定! 請等待其他人允許您進入...",
"room.lobbyAdministration": "大廳管理",
"room.peersInLobby": "大廳的參與者",
"room.lobbyEmpty": "大廳目前沒有人",
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
"room.me": "我",
"room.spotlights": "Spotlight中的參與者",
"room.passive": "被動參與者",
"room.videoPaused": "視訊已關閉",
"room.muteAll": "全部靜音",
"room.stopAllVideo": "關閉全部視訊",
"room.closeMeeting": "關閉會議",
"room.clearChat": "清除聊天",
"room.clearFileSharing": "清除檔案",
"room.speechUnsupported": "您的瀏覽器不支援語音辨識",
"room.moderatoractions": "管理員動作",
"room.raisedHand": "{displayName} 舉手了",
"room.loweredHand": "{displayName} 放下了他的手",
"room.extraVideo": "其他視訊",
"room.overRoomLimit": "房間已滿,請稍後重試",
"room.help": "幫助",
"room.about": "關於",
"room.shortcutKeys": "鍵盤快速鍵",
"room.stopAllScreenSharing": null,
"me.mutedPTT": "您已靜音,請按下 空白鍵 來說話",
"roles.gotRole": "您已取得身份: {role}",
"roles.lostRole": "您的 {role} 身份已被撤銷",
"tooltip.login": "登入",
"tooltip.logout": "登出",
"tooltip.admitFromLobby": "從大廳允許",
"tooltip.lockRoom": "鎖定房間",
"tooltip.unLockRoom": "解鎖房間",
"tooltip.enterFullscreen": "進入全螢幕",
"tooltip.leaveFullscreen": "退出全螢幕",
"tooltip.lobby": "顯示大廳",
"tooltip.settings": "顯示設置",
"tooltip.participants": "顯示參加者",
"tooltip.kickParticipant": "踢出",
"tooltip.muteParticipant": "靜音",
"tooltip.muteParticipantVideo": "隱藏視訊",
"tooltip.raisedHand": "舉手",
"tooltip.muteScreenSharing": "隱藏螢幕分享",
"tooltip.muteParticipantAudioModerator": "關閉聲音",
"tooltip.muteParticipantVideoModerator": "關閉視訊",
"tooltip.muteScreenSharingModerator": "關閉螢幕分享",
"label.roomName": "房間名稱",
"label.chooseRoomButton": "繼續",
"label.yourName": "您的名字",
"label.newWindow": "新視窗",
"label.fullscreen": "全螢幕",
"label.openDrawer": "打開側邊欄",
"label.leave": "離開",
"label.chatInput": "輸入聊天訊息",
"label.chat": "聊天",
"label.filesharing": "文件分享",
"label.participants": "參與者",
"label.shareFile": "分享文件",
"label.shareGalleryFile": "分享圖片",
"label.fileSharingUnsupported": "不支援文件分享",
"label.unknown": "未知",
"label.democratic": "使用者佈局",
"label.filmstrip": "投影片佈局",
"label.low": "低",
"label.medium": "中",
"label.high": "高 (HD)",
"label.veryHigh": "非常高 (FHD)",
"label.ultra": "超高 (UHD)",
"label.close": "關閉",
"label.media": "媒體",
"label.appearance": "外觀",
"label.advanced": "進階",
"label.addVideo": "新增視訊",
"label.promoteAllPeers": "提升全部",
"label.moreActions": "更多",
"label.version": null,
"settings.settings": "設置",
"settings.camera": "視訊來源",
"settings.selectCamera": "選擇視訊來源",
"settings.cantSelectCamera": "無法選擇此視訊來源",
"settings.audio": "音訊來源",
"settings.selectAudio": "選擇音訊來源",
"settings.cantSelectAudio": "無法選擇音訊來源",
"settings.audioOutput": "音訊輸出",
"settings.selectAudioOutput": "選擇音訊輸出設備",
"settings.cantSelectAudioOutput": "無法選擇音訊輸出設備",
"settings.resolution": "選擇視訊解析度",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "房間佈局",
"settings.selectRoomLayout": "選擇房間佈局",
"settings.advancedMode": "進階模式",
"settings.permanentTopBar": "固定頂端列",
"settings.lastn": "視訊數量上限",
"settings.hiddenControls": "隱藏控制按鈕",
"settings.notificationSounds": "通知音效",
"settings.showNotifications": "顯示通知",
"settings.buttonControlBar": "獨立控制按鈕",
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": "回音消除",
"settings.autoGainControl": "自動增益控制",
"settings.noiseSuppression": "噪音消除",
"settings.drawerOverlayed": "側邊欄覆蓋畫面",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "無法保存文件",
"filesharing.startingFileShare": "開始分享文件",
"filesharing.successfulFileShare": "文件已成功分享",
"filesharing.unableToShare": "無法分享文件",
"filesharing.error": "文件分享發生錯誤",
"filesharing.finished": "文件分享成功",
"filesharing.save": "保存文件",
"filesharing.sharedFile": "{displayName} 分享了一個文件",
"filesharing.download": "下載文件",
"filesharing.missingSeeds": "如果過了很久還是無法下載,則可能沒有人播種了。請讓上傳者重新上傳您想要的文件。",
"devices.devicesChanged": "您的設備已更改,請在設置中設定您的設備",
"device.audioUnsupported": "不支援您的音訊格式",
"device.activateAudio": "開啟音訊",
"device.muteAudio": "靜音",
"device.unMuteAudio": "取消靜音",
"device.videoUnsupported": "不支援您的視訊格式",
"device.startVideo": "開啟視訊",
"device.stopVideo": "關閉視訊",
"device.screenSharingUnsupported": "不支援您的螢幕分享格式",
"device.startScreenSharing": "開始螢幕分享",
"device.stopScreenSharing": "停止螢幕分享",
"devices.microphoneDisconnected": "麥克風已斷開",
"devices.microphoneError": "麥克風發生錯誤",
"devices.microphoneMute": "麥克風靜音",
"devices.microphoneUnMute": "取消麥克風靜音",
"devices.microphoneEnable": "麥克風已啟用",
"devices.microphoneMuteError": "無法使麥克風靜音",
"devices.microphoneUnMuteError": "無法取消麥克風靜音",
"devices.screenSharingDisconnected" : "螢幕分享已斷開",
"devices.screenSharingError": "螢幕分享時發生錯誤",
"devices.cameraDisconnected": "相機已斷開連接",
"devices.cameraError": "存取相機時發生錯誤",
"moderator.clearChat": "管理員清除了聊天",
"moderator.clearFiles": "管理員清除了所有檔案",
"moderator.muteAudio": "您已被管理員靜音",
"moderator.muteVideo": "您的視訊已被管理員關閉",
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -49,27 +49,6 @@
"room.spotlights": "Учасники у центрі уваги",
"room.passive": "Пасивні учасники",
"room.videoPaused": "Це відео призупинено",
"room.muteAll": null,
"room.stopAllVideo": null,
"room.closeMeeting": null,
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
"room.moderatoractions": null,
"room.raisedHand": null,
"room.loweredHand": null,
"room.extraVideo": null,
"room.overRoomLimit": null,
"room.help": null,
"room.about": null,
"room.shortcutKeys": null,
"room.browsePeersSpotlight": null,
"room.stopAllScreenSharing": null,
"me.mutedPTT": null,
"roles.gotRole": null,
"roles.lostRole": null,
"tooltip.login": "Увійти",
"tooltip.logout": "Вихід",
@ -81,14 +60,6 @@
"tooltip.lobby": "Показати зал очікувань",
"tooltip.settings": "Показати налаштування",
"tooltip.participants": "Показати учасників",
"tooltip.kickParticipant": null,
"tooltip.muteParticipant": null,
"tooltip.muteParticipantVideo": null,
"tooltip.raisedHand": null,
"tooltip.muteScreenSharing": null,
"tooltip.muteParticipantAudioModerator": null,
"tooltip.muteParticipantVideoModerator": null,
"tooltip.muteScreenSharingModerator": null,
"label.roomName": "Назва кімнати",
"label.chooseRoomButton": "Продовжити",
@ -102,7 +73,6 @@
"label.filesharing": "Обмін файлами",
"label.participants": "Учасники",
"label.shareFile": "Надіслати файл",
"label.shareGalleryFile": null,
"label.fileSharingUnsupported": "Обмін файлами не підтримується",
"label.unknown": "Невідомо",
"label.democrat": "Демократичний вигляд",
@ -113,13 +83,6 @@
"label.veryHigh": "Дуже високий (FHD)",
"label.ultra": "Ультра (UHD)",
"label.close": "Закрити",
"label.media": null,
"label.appearance": null,
"label.advanced": null,
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Налаштування",
"settings.camera": "Камера",
@ -128,30 +91,12 @@
"settings.audio": "Аудіопристрій",
"settings.selectAudio": "Вибрати аудіопристрій",
"settings.cantSelectAudio": "Неможливо вибрати аудіопристрій",
"settings.audioOutput": "Пристрій аудіовиходу",
"settings.selectAudioOutput": "Виберіть пристрій аудіовиходу",
"settings.cantSelectAudioOutput": "Неможливо вибрати аудіо вихідний пристрій",
"settings.resolution": "Виберіть роздільну здатність відео",
"settings.frameRate": null,
"settings.screenSharingResolution": null,
"settings.screenSharingFrameRate": null,
"settings.layout": "Розміщення кімнати",
"settings.selectRoomLayout": "Вибір розташування кімнати",
"settings.advancedMode": "Розширений режим",
"settings.permanentTopBar": "Постійний верхній рядок",
"settings.lastn": "Кількість видимих ​​відео",
"settings.hiddenControls": null,
"settings.notificationSounds": null,
"settings.showNotifications": null,
"settings.buttonControlBar": null,
"settings.showAdvancedVideo": null,
"settings.showAdvancedAudio": null,
"settings.echoCancellation": null,
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Неможливо зберегти файл",
"filesharing.startingFileShare": "Спроба поділитися файлом",
@ -191,15 +136,5 @@
"devices.screenSharingError": "Сталася помилка під час доступу до екрану",
"devices.cameraDisconnected": "Камера відключена",
"devices.cameraError": "Під час доступу до камери сталася помилка",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
"devices.cameraError": "Під час доступу до камери сталася помилка"
}

View File

@ -1,12 +1,14 @@
export function getSignalingUrl(peerId, roomId)
{
const hostname = window.config.multipartyServer;
const port =
process.env.NODE_ENV !== 'production' ?
window.config.developmentPort
:
window.config.productionPort;
const url = `wss://${window.location.hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
const url = `wss://${hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
return url;
}

Some files were not shown because too many files have changed in this diff Show More