Merge branch 'develop'
commit
46a2c075f0
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -1,5 +1,40 @@
|
||||||
# Changelog
|
# 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
|
## 3.2.1
|
||||||
|
|
||||||
* Fix: permananent top bar by default
|
* Fix: permananent top bar by default
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Source code contributions should pass static code analysis as performed by `npm run lint` in `server` and `app` respectively.
|
||||||
|
|
@ -62,14 +62,6 @@ OR
|
||||||
|
|
||||||
## Configure multiparty-meeting servers
|
## Configure multiparty-meeting servers
|
||||||
|
|
||||||
### App config
|
|
||||||
|
|
||||||
mm/configs/app/config.js
|
|
||||||
|
|
||||||
``` js
|
|
||||||
multipartyServer : 'meet.example.com',
|
|
||||||
```
|
|
||||||
|
|
||||||
### Server config
|
### Server config
|
||||||
|
|
||||||
mm/configs/server/config.js
|
mm/configs/server/config.js
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 GÉANT Association
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -23,11 +23,11 @@ If you want the ansible approach, you can find ansible role [here](https://githu
|
||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
* Prerequisites:
|
* Prerequisites:
|
||||||
Currently multiparty-meeting will only run on nodejs v10.*
|
Currently multiparty-meeting will only run on nodejs v13.x
|
||||||
To install see here [here](https://github.com/nodesource/distributions/blob/master/README.md#debinstall).
|
To install see here [here](https://github.com/nodesource/distributions/blob/master/README.md#debinstall).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ sudo apt install npm build-essential redis
|
$ sudo apt install git npm build-essential redis
|
||||||
```
|
```
|
||||||
|
|
||||||
* Clone the project:
|
* 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
|
## 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 `app/public/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 `server/config/config.js`
|
||||||
|
|
||||||
## Community-driven support
|
## Community-driven support
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ This started as a fork of the [work](https://github.com/versatica/mediasoup-demo
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT License (see `LICENSE.md`)
|
||||||
|
|
||||||
|
|
||||||
Contributions to this work were made on behalf of the GÉANT project, a project that has received funding from the European Union’s Horizon 2020 research and innovation programme under Grant Agreement No. 731122 (GN4-2). On behalf of GÉANT project, GÉANT Association is the sole owner of the copyright in all material which was developed by a member of the GÉANT project.
|
Contributions to this work were made on behalf of the GÉANT project, a project that has received funding from the European Union’s Horizon 2020 research and innovation programme under Grant Agreement No. 731122 (GN4-2). On behalf of GÉANT project, GÉANT Association is the sole owner of the copyright in all material which was developed by a member of the GÉANT project.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
REACT_APP_VERSION=$npm_package_version
|
||||||
|
REACT_APP_NAME=$npm_package_name
|
||||||
|
|
@ -159,6 +159,12 @@
|
||||||
"no-inner-declarations": 2,
|
"no-inner-declarations": 2,
|
||||||
"no-invalid-regexp": 2,
|
"no-invalid-regexp": 2,
|
||||||
"no-irregular-whitespace": 2,
|
"no-irregular-whitespace": 2,
|
||||||
|
"no-trailing-spaces": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"ignoreComments": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"no-lonely-if": 2,
|
"no-lonely-if": 2,
|
||||||
"no-mixed-operators": 2,
|
"no-mixed-operators": 2,
|
||||||
"no-mixed-spaces-and-tabs": 2,
|
"no-mixed-spaces-and-tabs": 2,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "multiparty-meeting",
|
"name": "multiparty-meeting",
|
||||||
"version": "3.2.1",
|
"version": "3.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "multiparty meeting service",
|
"description": "multiparty meeting service",
|
||||||
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
||||||
|
|
@ -20,17 +20,19 @@
|
||||||
"hark": "^1.2.3",
|
"hark": "^1.2.3",
|
||||||
"is-electron": "^2.2.0",
|
"is-electron": "^2.2.0",
|
||||||
"marked": "^0.8.0",
|
"marked": "^0.8.0",
|
||||||
"mediasoup-client": "^3.5.4",
|
"mediasoup-client": "^3.6.5",
|
||||||
"notistack": "^0.9.5",
|
"notistack": "^0.9.5",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"random-string": "^0.2.0",
|
"random-string": "^0.2.0",
|
||||||
"react": "^16.10.2",
|
"react": "^16.10.2",
|
||||||
"react-cookie-consent": "^2.5.0",
|
"react-cookie-consent": "^2.5.0",
|
||||||
"react-dom": "^16.10.2",
|
"react-dom": "^16.10.2",
|
||||||
|
"react-flip-toolkit": "^7.0.9",
|
||||||
"react-intl": "^3.4.0",
|
"react-intl": "^3.4.0",
|
||||||
"react-redux": "^7.1.1",
|
"react-redux": "^7.1.1",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-scripts": "3.0.1",
|
"react-scripts": "3.4.1",
|
||||||
|
"react-wakelock-react16": "0.0.7",
|
||||||
"redux": "^4.0.4",
|
"redux": "^4.0.4",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
|
|
@ -48,7 +50,8 @@
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"electron": "electron --no-sandbox .",
|
"electron": "electron --no-sandbox .",
|
||||||
"dev": "nf start -p 3000"
|
"dev": "nf start -p 3000",
|
||||||
|
"lint": "eslint -c .eslintrc.json --ext .js src"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
|
@ -58,6 +61,7 @@
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^7.1.1",
|
"electron": "^7.1.1",
|
||||||
|
"eslint-plugin-react": "^7.19.0",
|
||||||
"foreman": "^3.0.1",
|
"foreman": "^3.0.1",
|
||||||
"redux-mock-store": "^1.5.3"
|
"redux-mock-store": "^1.5.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,148 @@
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
var config =
|
var config =
|
||||||
{
|
{
|
||||||
loginEnabled : false,
|
loginEnabled : false,
|
||||||
developmentPort : 3443,
|
developmentPort : 3443,
|
||||||
productionPort : 443,
|
productionPort : 443,
|
||||||
multipartyServer : 'letsmeet.no',
|
|
||||||
turnServers : [
|
|
||||||
{
|
|
||||||
urls : [
|
|
||||||
'turn:turn.example.com:443?transport=tcp'
|
|
||||||
],
|
|
||||||
username : 'example',
|
|
||||||
credential : 'example'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
/**
|
/**
|
||||||
* If defaultResolution is set, it will override user settings when joining:
|
* 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 :
|
||||||
|
{
|
||||||
|
'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'
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolutions:
|
||||||
|
*
|
||||||
* low ~ 320x240
|
* low ~ 320x240
|
||||||
* medium ~ 640x480
|
* medium ~ 640x480
|
||||||
* high ~ 1280x720
|
* high ~ 1280x720
|
||||||
* veryhigh ~ 1920x1080
|
* veryhigh ~ 1920x1080
|
||||||
* ultra ~ 3840x2560
|
* ultra ~ 3840x2560
|
||||||
|
*
|
||||||
**/
|
**/
|
||||||
defaultResolution : 'medium',
|
|
||||||
|
/**
|
||||||
|
* Frame rates:
|
||||||
|
*
|
||||||
|
* 1, 5, 10, 15, 20, 25, 30
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
defaultResolution : 'medium',
|
||||||
|
defaultFrameRate : 15,
|
||||||
|
defaultScreenResolution : 'veryhigh',
|
||||||
|
defaultScreenSharingFrameRate : 5,
|
||||||
// Enable or disable simulcast for webcam video
|
// Enable or disable simulcast for webcam video
|
||||||
simulcast : true,
|
simulcast : true,
|
||||||
// Enable or disable simulcast for screen sharing video
|
// Enable or disable simulcast for screen sharing video
|
||||||
simulcastSharing : false,
|
simulcastSharing : false,
|
||||||
// Simulcast encoding layers and levels
|
// Simulcast encoding layers and levels
|
||||||
simulcastEncodings :
|
simulcastEncodings :
|
||||||
[
|
[
|
||||||
{ scaleResolutionDownBy: 4 },
|
{ scaleResolutionDownBy: 4 },
|
||||||
{ scaleResolutionDownBy: 2 },
|
{ scaleResolutionDownBy: 2 },
|
||||||
{ scaleResolutionDownBy: 1 }
|
{ 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
|
// Socket.io request timeout
|
||||||
requestTimeout : 10000,
|
requestTimeout : 20000,
|
||||||
transportOptions :
|
requestRetries : 3,
|
||||||
|
transportOptions :
|
||||||
{
|
{
|
||||||
tcp : true
|
tcp : true
|
||||||
},
|
},
|
||||||
lastN : 4,
|
defaultAudio :
|
||||||
mobileLastN : 1,
|
{
|
||||||
background : 'images/background.jpg',
|
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,
|
||||||
// Add file and uncomment for adding logo to appbar
|
// Add file and uncomment for adding logo to appbar
|
||||||
// logo : 'images/logo.svg',
|
// logo : 'images/logo.svg',
|
||||||
title : 'Multiparty meeting',
|
title : 'Multiparty meeting',
|
||||||
theme :
|
// 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 :
|
palette :
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,44 @@
|
||||||
<title>Multiparty Meeting</title>
|
<title>Multiparty Meeting</title>
|
||||||
|
|
||||||
<script src='%PUBLIC_URL%/config/config.js' type='text/javascript'></script>
|
<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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!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
|
|
@ -213,6 +213,8 @@ export default class ScreenShare
|
||||||
{
|
{
|
||||||
if (isElectron())
|
if (isElectron())
|
||||||
return new ElectronScreenShare();
|
return new ElectronScreenShare();
|
||||||
|
else if (device.platform !== 'desktop')
|
||||||
|
return new DefaultScreenShare();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (device.flag)
|
switch (device.flag)
|
||||||
|
|
@ -224,11 +226,17 @@ export default class ScreenShare
|
||||||
else
|
else
|
||||||
return new DisplayMediaScreenShare();
|
return new DisplayMediaScreenShare();
|
||||||
}
|
}
|
||||||
case 'chrome':
|
case 'safari':
|
||||||
{
|
{
|
||||||
return new DisplayMediaScreenShare();
|
if (device.version >= 13.0)
|
||||||
|
return new DisplayMediaScreenShare();
|
||||||
|
else
|
||||||
|
return new DefaultScreenShare();
|
||||||
}
|
}
|
||||||
case 'msedge':
|
case 'chrome':
|
||||||
|
case 'chromium':
|
||||||
|
case 'opera':
|
||||||
|
case 'edge':
|
||||||
{
|
{
|
||||||
return new DisplayMediaScreenShare();
|
return new DisplayMediaScreenShare();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export default class Spotlights extends EventEmitter
|
||||||
this._signalingSocket = signalingSocket;
|
this._signalingSocket = signalingSocket;
|
||||||
this._maxSpotlights = maxSpotlights;
|
this._maxSpotlights = maxSpotlights;
|
||||||
this._peerList = [];
|
this._peerList = [];
|
||||||
|
this._unmutablePeerList = [];
|
||||||
this._selectedSpotlights = [];
|
this._selectedSpotlights = [];
|
||||||
this._currentSpotlights = [];
|
this._currentSpotlights = [];
|
||||||
this._started = false;
|
this._started = false;
|
||||||
|
|
@ -45,6 +46,74 @@ 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)
|
setPeerSpotlight(peerId)
|
||||||
{
|
{
|
||||||
logger.debug('setPeerSpotlight() [peerId:"%s"]', peerId);
|
logger.debug('setPeerSpotlight() [peerId:"%s"]', peerId);
|
||||||
|
|
@ -95,6 +164,15 @@ export default class Spotlights extends EventEmitter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearSpotlights()
|
||||||
|
{
|
||||||
|
this._started = false;
|
||||||
|
|
||||||
|
this._peerList = [];
|
||||||
|
this._selectedSpotlights = [];
|
||||||
|
this._currentSpotlights = [];
|
||||||
|
}
|
||||||
|
|
||||||
_newPeer(id)
|
_newPeer(id)
|
||||||
{
|
{
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|
@ -105,6 +183,7 @@ export default class Spotlights extends EventEmitter
|
||||||
logger.debug('_handlePeer() | adding peer [peerId: "%s"]', id);
|
logger.debug('_handlePeer() | adding peer [peerId: "%s"]', id);
|
||||||
|
|
||||||
this._peerList.push(id);
|
this._peerList.push(id);
|
||||||
|
this._unmutablePeerList.push(id);
|
||||||
|
|
||||||
if (this._started)
|
if (this._started)
|
||||||
this._spotlightsUpdated();
|
this._spotlightsUpdated();
|
||||||
|
|
@ -116,19 +195,10 @@ export default class Spotlights extends EventEmitter
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'room "peerClosed" event [peerId:%o]', id);
|
'room "peerClosed" event [peerId:%o]', id);
|
||||||
|
|
||||||
let index = this._peerList.indexOf(id);
|
this._peerList = this._peerList.filter((peer) => peer !== id);
|
||||||
|
this._unmutablePeerList = this._unmutablePeerList.filter((peer) => peer !== id);
|
||||||
|
|
||||||
if (index !== -1) // We have this peer in the list, remove
|
this._selectedSpotlights = this._selectedSpotlights.filter((peer) => peer !== id);
|
||||||
{
|
|
||||||
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)
|
if (this._started)
|
||||||
this._spotlightsUpdated();
|
this._spotlightsUpdated();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ beforeEach(() =>
|
||||||
me : {
|
me : {
|
||||||
audioDevices : null,
|
audioDevices : null,
|
||||||
audioInProgress : false,
|
audioInProgress : false,
|
||||||
|
audioOutputDevices : null,
|
||||||
|
audioOutputInProgress : false,
|
||||||
canSendMic : false,
|
canSendMic : false,
|
||||||
canSendWebcam : false,
|
canSendWebcam : false,
|
||||||
canShareFiles : false,
|
canShareFiles : false,
|
||||||
|
|
@ -40,8 +42,8 @@ beforeEach(() =>
|
||||||
loggedIn : false,
|
loggedIn : false,
|
||||||
loginEnabled : true,
|
loginEnabled : true,
|
||||||
picture : null,
|
picture : null,
|
||||||
raiseHand : false,
|
raisedHand : false,
|
||||||
raiseHandInProgress : false,
|
raisedHandInProgress : false,
|
||||||
screenShareInProgress : false,
|
screenShareInProgress : false,
|
||||||
webcamDevices : null,
|
webcamDevices : null,
|
||||||
webcamInProgress : false
|
webcamInProgress : false
|
||||||
|
|
@ -72,11 +74,12 @@ beforeEach(() =>
|
||||||
windowConsumer : null
|
windowConsumer : null
|
||||||
},
|
},
|
||||||
settings : {
|
settings : {
|
||||||
advancedMode : true,
|
advancedMode : true,
|
||||||
displayName : 'Jest Tester',
|
displayName : 'Jest Tester',
|
||||||
resolution : 'ultra',
|
resolution : 'ultra',
|
||||||
selectedAudioDevice : 'default',
|
selectedAudioDevice : 'default',
|
||||||
selectedWebcam : 'soifjsiajosjfoi'
|
selectedAudioOutputDevice : 'default',
|
||||||
|
selectedWebcam : 'soifjsiajosjfoi'
|
||||||
},
|
},
|
||||||
toolarea : {
|
toolarea : {
|
||||||
currentToolTab : 'chat',
|
currentToolTab : 'chat',
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,8 @@ export const addChatHistory = (chatHistory) =>
|
||||||
type : 'ADD_CHAT_HISTORY',
|
type : 'ADD_CHAT_HISTORY',
|
||||||
payload : { chatHistory }
|
payload : { chatHistory }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearChat = () =>
|
||||||
|
({
|
||||||
|
type : 'CLEAR_CHAT'
|
||||||
|
});
|
||||||
|
|
@ -10,6 +10,11 @@ export const removeConsumer = (consumerId, peerId) =>
|
||||||
payload : { consumerId, peerId }
|
payload : { consumerId, peerId }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearConsumers = () =>
|
||||||
|
({
|
||||||
|
type : 'CLEAR_CONSUMERS'
|
||||||
|
});
|
||||||
|
|
||||||
export const setConsumerPaused = (consumerId, originator) =>
|
export const setConsumerPaused = (consumerId, originator) =>
|
||||||
({
|
({
|
||||||
type : 'SET_CONSUMER_PAUSED',
|
type : 'SET_CONSUMER_PAUSED',
|
||||||
|
|
@ -35,12 +40,10 @@ export const setConsumerPreferredLayers = (consumerId, spatialLayer, temporalLay
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setConsumerPriority = (consumerId, priority) =>
|
export const setConsumerPriority = (consumerId, priority) =>
|
||||||
{
|
({
|
||||||
return {
|
type : 'SET_CONSUMER_PRIORITY',
|
||||||
type : 'SET_CONSUMER_PRIORITY',
|
payload : { consumerId, priority }
|
||||||
payload : { consumerId, priority }
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setConsumerTrack = (consumerId, track) =>
|
export const setConsumerTrack = (consumerId, track) =>
|
||||||
({
|
({
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,8 @@ export const setFileDone = (magnetUri, sharedFiles) =>
|
||||||
type : 'SET_FILE_DONE',
|
type : 'SET_FILE_DONE',
|
||||||
payload : { magnetUri, sharedFiles }
|
payload : { magnetUri, sharedFiles }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearFiles = () =>
|
||||||
|
({
|
||||||
|
type : 'CLEAR_FILES'
|
||||||
|
});
|
||||||
|
|
@ -4,12 +4,30 @@ export const setMe = ({ peerId, loginEnabled }) =>
|
||||||
payload : { peerId, loginEnabled }
|
payload : { peerId, loginEnabled }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setBrowser = (browser) =>
|
||||||
|
({
|
||||||
|
type : 'SET_BROWSER',
|
||||||
|
payload : { browser }
|
||||||
|
});
|
||||||
|
|
||||||
export const loggedIn = (flag) =>
|
export const loggedIn = (flag) =>
|
||||||
({
|
({
|
||||||
type : 'LOGGED_IN',
|
type : 'LOGGED_IN',
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const addRole = (role) =>
|
||||||
|
({
|
||||||
|
type : 'ADD_ROLE',
|
||||||
|
payload : { role }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeRole = (role) =>
|
||||||
|
({
|
||||||
|
type : 'REMOVE_ROLE',
|
||||||
|
payload : { role }
|
||||||
|
});
|
||||||
|
|
||||||
export const setPicture = (picture) =>
|
export const setPicture = (picture) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PICTURE',
|
type : 'SET_PICTURE',
|
||||||
|
|
@ -17,11 +35,11 @@ export const setPicture = (picture) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setMediaCapabilities = ({
|
export const setMediaCapabilities = ({
|
||||||
canSendMic,
|
canSendMic,
|
||||||
canSendWebcam,
|
canSendWebcam,
|
||||||
canShareScreen,
|
canShareScreen,
|
||||||
canShareFiles
|
canShareFiles
|
||||||
}) =>
|
}) =>
|
||||||
({
|
({
|
||||||
type : 'SET_MEDIA_CAPABILITIES',
|
type : 'SET_MEDIA_CAPABILITIES',
|
||||||
payload : { canSendMic, canSendWebcam, canShareScreen, canShareFiles }
|
payload : { canSendMic, canSendWebcam, canShareScreen, canShareFiles }
|
||||||
|
|
@ -33,15 +51,21 @@ export const setAudioDevices = (devices) =>
|
||||||
payload : { devices }
|
payload : { devices }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setAudioOutputDevices = (devices) =>
|
||||||
|
({
|
||||||
|
type : 'SET_AUDIO_OUTPUT_DEVICES',
|
||||||
|
payload : { devices }
|
||||||
|
});
|
||||||
|
|
||||||
export const setWebcamDevices = (devices) =>
|
export const setWebcamDevices = (devices) =>
|
||||||
({
|
({
|
||||||
type : 'SET_WEBCAM_DEVICES',
|
type : 'SET_WEBCAM_DEVICES',
|
||||||
payload : { devices }
|
payload : { devices }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setMyRaiseHandState = (flag) =>
|
export const setRaisedHand = (flag) =>
|
||||||
({
|
({
|
||||||
type : 'SET_MY_RAISE_HAND_STATE',
|
type : 'SET_RAISED_HAND',
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -51,6 +75,12 @@ export const setAudioInProgress = (flag) =>
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setAudioOutputInProgress = (flag) =>
|
||||||
|
({
|
||||||
|
type : 'SET_AUDIO_OUTPUT_IN_PROGRESS',
|
||||||
|
payload : { flag }
|
||||||
|
});
|
||||||
|
|
||||||
export const setWebcamInProgress = (flag) =>
|
export const setWebcamInProgress = (flag) =>
|
||||||
({
|
({
|
||||||
type : 'SET_WEBCAM_IN_PROGRESS',
|
type : 'SET_WEBCAM_IN_PROGRESS',
|
||||||
|
|
@ -63,9 +93,9 @@ export const setScreenShareInProgress = (flag) =>
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setMyRaiseHandStateInProgress = (flag) =>
|
export const setRaisedHandInProgress = (flag) =>
|
||||||
({
|
({
|
||||||
type : 'SET_MY_RAISE_HAND_STATE_IN_PROGRESS',
|
type : 'SET_RAISED_HAND_IN_PROGRESS',
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -74,3 +104,15 @@ export const setDisplayNameInProgress = (flag) =>
|
||||||
type : 'SET_DISPLAY_NAME_IN_PROGRESS',
|
type : 'SET_DISPLAY_NAME_IN_PROGRESS',
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setIsSpeaking = (flag) =>
|
||||||
|
({
|
||||||
|
type : 'SET_IS_SPEAKING',
|
||||||
|
payload : { flag }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setAutoMuted = (flag) =>
|
||||||
|
({
|
||||||
|
type : 'SET_AUTO_MUTED',
|
||||||
|
payload : { flag }
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,11 @@ export const removePeer = (peerId) =>
|
||||||
payload : { peerId }
|
payload : { peerId }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearPeers = () =>
|
||||||
|
({
|
||||||
|
type : 'CLEAR_PEERS'
|
||||||
|
});
|
||||||
|
|
||||||
export const setPeerDisplayName = (displayName, peerId) =>
|
export const setPeerDisplayName = (displayName, peerId) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PEER_DISPLAY_NAME',
|
type : 'SET_PEER_DISPLAY_NAME',
|
||||||
|
|
@ -34,10 +39,16 @@ export const setPeerScreenInProgress = (peerId, flag) =>
|
||||||
payload : { peerId, flag }
|
payload : { peerId, flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPeerRaiseHandState = (peerId, raiseHandState) =>
|
export const setPeerRaisedHand = (peerId, raisedHand, raisedHandTimestamp) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PEER_RAISE_HAND_STATE',
|
type : 'SET_PEER_RAISED_HAND',
|
||||||
payload : { peerId, raiseHandState }
|
payload : { peerId, raisedHand, raisedHandTimestamp }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setPeerRaisedHandInProgress = (peerId, flag) =>
|
||||||
|
({
|
||||||
|
type : 'SET_PEER_RAISED_HAND_IN_PROGRESS',
|
||||||
|
payload : { peerId, flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPeerPicture = (peerId, picture) =>
|
export const setPeerPicture = (peerId, picture) =>
|
||||||
|
|
@ -45,3 +56,39 @@ export const setPeerPicture = (peerId, picture) =>
|
||||||
type : 'SET_PEER_PICTURE',
|
type : 'SET_PEER_PICTURE',
|
||||||
payload : { peerId, 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 }
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,12 @@ export const setSignInRequired = (signInRequired) =>
|
||||||
payload : { signInRequired }
|
payload : { signInRequired }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setOverRoomLimit = (overRoomLimit) =>
|
||||||
|
({
|
||||||
|
type : 'SET_OVER_ROOM_LIMIT',
|
||||||
|
payload : { overRoomLimit }
|
||||||
|
});
|
||||||
|
|
||||||
export const setAccessCode = (accessCode) =>
|
export const setAccessCode = (accessCode) =>
|
||||||
({
|
({
|
||||||
type : 'SET_ACCESS_CODE',
|
type : 'SET_ACCESS_CODE',
|
||||||
|
|
@ -52,13 +58,37 @@ export const setJoinByAccessCode = (joinByAccessCode) =>
|
||||||
payload : { joinByAccessCode }
|
payload : { joinByAccessCode }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setSettingsOpen = ({ settingsOpen }) =>
|
export const setSettingsOpen = (settingsOpen) =>
|
||||||
({
|
({
|
||||||
type : 'SET_SETTINGS_OPEN',
|
type : 'SET_SETTINGS_OPEN',
|
||||||
payload : { settingsOpen }
|
payload : { settingsOpen }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setLockDialogOpen = ({ lockDialogOpen }) =>
|
export const setExtraVideoOpen = (extraVideoOpen) =>
|
||||||
|
({
|
||||||
|
type : 'SET_EXTRA_VIDEO_OPEN',
|
||||||
|
payload : { extraVideoOpen }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const 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) =>
|
||||||
({
|
({
|
||||||
type : 'SET_LOCK_DIALOG_OPEN',
|
type : 'SET_LOCK_DIALOG_OPEN',
|
||||||
payload : { lockDialogOpen }
|
payload : { lockDialogOpen }
|
||||||
|
|
@ -100,6 +130,11 @@ export const setSpotlights = (spotlights) =>
|
||||||
payload : { spotlights }
|
payload : { spotlights }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearSpotlights = () =>
|
||||||
|
({
|
||||||
|
type : 'CLEAR_SPOTLIGHTS'
|
||||||
|
});
|
||||||
|
|
||||||
export const toggleJoined = () =>
|
export const toggleJoined = () =>
|
||||||
({
|
({
|
||||||
type : 'TOGGLE_JOINED'
|
type : 'TOGGLE_JOINED'
|
||||||
|
|
@ -110,3 +145,57 @@ export const toggleConsumerFullscreen = (consumerId) =>
|
||||||
type : 'TOGGLE_FULLSCREEN_CONSUMER',
|
type : 'TOGGLE_FULLSCREEN_CONSUMER',
|
||||||
payload : { consumerId }
|
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 }
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,12 @@ export const setSelectedAudioDevice = (deviceId) =>
|
||||||
payload : { deviceId }
|
payload : { deviceId }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setSelectedAudioOutputDevice = (deviceId) =>
|
||||||
|
({
|
||||||
|
type : 'CHANGE_AUDIO_OUTPUT_DEVICE',
|
||||||
|
payload : { deviceId }
|
||||||
|
});
|
||||||
|
|
||||||
export const setSelectedWebcamDevice = (deviceId) =>
|
export const setSelectedWebcamDevice = (deviceId) =>
|
||||||
({
|
({
|
||||||
type : 'CHANGE_WEBCAM',
|
type : 'CHANGE_WEBCAM',
|
||||||
|
|
@ -16,6 +22,24 @@ export const setVideoResolution = (resolution) =>
|
||||||
payload : { 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) =>
|
export const setDisplayName = (displayName) =>
|
||||||
({
|
({
|
||||||
type : 'SET_DISPLAY_NAME',
|
type : 'SET_DISPLAY_NAME',
|
||||||
|
|
@ -32,6 +56,67 @@ export const togglePermanentTopBar = () =>
|
||||||
type : 'TOGGLE_PERMANENT_TOPBAR'
|
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) =>
|
export const setLastN = (lastN) =>
|
||||||
({
|
({
|
||||||
type : 'SET_LAST_N',
|
type : 'SET_LAST_N',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export const addTransportStats = (transport, type) =>
|
||||||
|
({
|
||||||
|
type : 'ADD_TRANSPORT_STATS',
|
||||||
|
payload : { transport, type }
|
||||||
|
});
|
||||||
|
|
@ -5,74 +5,20 @@ import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
||||||
import PromoteIcon from '@material-ui/icons/OpenInBrowser';
|
import PromoteIcon from '@material-ui/icons/OpenInBrowser';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = () =>
|
||||||
({
|
({
|
||||||
root :
|
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'
|
alignItems : 'center'
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +29,8 @@ const ListLobbyPeer = (props) =>
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
peer,
|
peer,
|
||||||
|
promotionInProgress,
|
||||||
|
canPromote,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
|
@ -92,7 +40,7 @@ const ListLobbyPeer = (props) =>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
className={classnames(classes.ListItem)}
|
className={classnames(classes.root)}
|
||||||
key={peer.peerId}
|
key={peer.peerId}
|
||||||
button
|
button
|
||||||
alignItems='flex-start'
|
alignItems='flex-start'
|
||||||
|
|
@ -109,10 +57,13 @@ const ListLobbyPeer = (props) =>
|
||||||
defaultMessage : 'Click to let them in'
|
defaultMessage : 'Click to let them in'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ListItemIcon
|
<IconButton
|
||||||
className={classnames(classes.button, 'promote', {
|
disabled={
|
||||||
disabled : peer.promotionInProgress
|
!canPromote ||
|
||||||
})}
|
peer.promotionInProgress ||
|
||||||
|
promotionInProgress
|
||||||
|
}
|
||||||
|
color='primary'
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -120,7 +71,7 @@ const ListLobbyPeer = (props) =>
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PromoteIcon />
|
<PromoteIcon />
|
||||||
</ListItemIcon>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
|
|
@ -128,27 +79,41 @@ const ListLobbyPeer = (props) =>
|
||||||
|
|
||||||
ListLobbyPeer.propTypes =
|
ListLobbyPeer.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
peer : PropTypes.object.isRequired,
|
peer : PropTypes.object.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
promotionInProgress : PropTypes.bool.isRequired,
|
||||||
|
canPromote : PropTypes.bool.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, { id }) =>
|
const makeMapStateToProps = (initialState, { id }) =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.PROMOTE_PEER);
|
||||||
peer : state.lobbyPeers[id]
|
|
||||||
|
const mapStateToProps = (state) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
peer : state.lobbyPeers[id],
|
||||||
|
promotionInProgress : state.room.lobbyPeersPromotionInProgress,
|
||||||
|
canPromote : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
|
prev.room === next.room &&
|
||||||
|
prev.peers === next.peers && // For checking permissions
|
||||||
|
prev.me.roles === next.me.roles &&
|
||||||
prev.lobbyPeers === next.lobbyPeers
|
prev.lobbyPeers === next.lobbyPeers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
lobbyPeersKeySelector
|
lobbyPeersKeySelector,
|
||||||
|
makePermissionSelector
|
||||||
} from '../../Selectors';
|
} from '../../Selectors';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
|
|
@ -15,14 +17,6 @@ import DialogActions from '@material-ui/core/DialogActions';
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent';
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
// import FormLabel from '@material-ui/core/FormLabel';
|
|
||||||
// import FormControl from '@material-ui/core/FormControl';
|
|
||||||
// import FormGroup from '@material-ui/core/FormGroup';
|
|
||||||
// import FormControlLabel from '@material-ui/core/FormControlLabel';
|
|
||||||
// import Checkbox from '@material-ui/core/Checkbox';
|
|
||||||
// import InputLabel from '@material-ui/core/InputLabel';
|
|
||||||
// import OutlinedInput from '@material-ui/core/OutlinedInput';
|
|
||||||
// import Switch from '@material-ui/core/Switch';
|
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||||
import ListLobbyPeer from './ListLobbyPeer';
|
import ListLobbyPeer from './ListLobbyPeer';
|
||||||
|
|
@ -59,11 +53,11 @@ const styles = (theme) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
const LockDialog = ({
|
const LockDialog = ({
|
||||||
// roomClient,
|
roomClient,
|
||||||
room,
|
room,
|
||||||
handleCloseLockDialog,
|
handleCloseLockDialog,
|
||||||
// handleAccessCode,
|
|
||||||
lobbyPeers,
|
lobbyPeers,
|
||||||
|
canPromote,
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
|
|
@ -71,7 +65,7 @@ const LockDialog = ({
|
||||||
<Dialog
|
<Dialog
|
||||||
className={classes.root}
|
className={classes.root}
|
||||||
open={room.lockDialogOpen}
|
open={room.lockDialogOpen}
|
||||||
onClose={() => handleCloseLockDialog({ lockDialogOpen: false })}
|
onClose={() => handleCloseLockDialog(false)}
|
||||||
classes={{
|
classes={{
|
||||||
paper : classes.dialogPaper
|
paper : classes.dialogPaper
|
||||||
}}
|
}}
|
||||||
|
|
@ -82,54 +76,6 @@ const LockDialog = ({
|
||||||
defaultMessage='Lobby administration'
|
defaultMessage='Lobby administration'
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
{/*
|
|
||||||
<FormControl component='fieldset' className={classes.formControl}>
|
|
||||||
<FormLabel component='legend'>Room lock</FormLabel>
|
|
||||||
<FormGroup>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch checked={room.locked} onChange={() =>
|
|
||||||
{
|
|
||||||
if (room.locked)
|
|
||||||
{
|
|
||||||
roomClient.unlockRoom();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
roomClient.lockRoom();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>}
|
|
||||||
label='Lock'
|
|
||||||
/>
|
|
||||||
TODO: access code
|
|
||||||
<FormControlLabel disabled={ room.locked ? false : true }
|
|
||||||
control={
|
|
||||||
<Checkbox checked={room.joinByAccessCode}
|
|
||||||
onChange={
|
|
||||||
(event) => roomClient.setJoinByAccessCode(event.target.checked)
|
|
||||||
}
|
|
||||||
/>}
|
|
||||||
label='Join by Access code'
|
|
||||||
/>
|
|
||||||
<InputLabel htmlFor='access-code-input' />
|
|
||||||
<OutlinedInput
|
|
||||||
disabled={ room.locked ? false : true }
|
|
||||||
id='acces-code-input'
|
|
||||||
label='Access code'
|
|
||||||
labelWidth={0}
|
|
||||||
variant='outlined'
|
|
||||||
value={room.accessCode}
|
|
||||||
onChange={(event) => handleAccessCode(event.target.value)}
|
|
||||||
>
|
|
||||||
</OutlinedInput>
|
|
||||||
<Button onClick={() => roomClient.setAccessCode(room.accessCode)} color='primary'>
|
|
||||||
save
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</FormGroup>
|
|
||||||
</FormControl>
|
|
||||||
*/}
|
|
||||||
{ lobbyPeers.length > 0 ?
|
{ lobbyPeers.length > 0 ?
|
||||||
<List
|
<List
|
||||||
dense
|
dense
|
||||||
|
|
@ -160,7 +106,21 @@ const LockDialog = ({
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
}
|
}
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => handleCloseLockDialog({ lockDialogOpen: false })} color='primary'>
|
<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'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='label.close'
|
id='label.close'
|
||||||
defaultMessage='Close'
|
defaultMessage='Close'
|
||||||
|
|
@ -173,20 +133,29 @@ const LockDialog = ({
|
||||||
|
|
||||||
LockDialog.propTypes =
|
LockDialog.propTypes =
|
||||||
{
|
{
|
||||||
// roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
handleCloseLockDialog : PropTypes.func.isRequired,
|
handleCloseLockDialog : PropTypes.func.isRequired,
|
||||||
handleAccessCode : PropTypes.func.isRequired,
|
handleAccessCode : PropTypes.func.isRequired,
|
||||||
lobbyPeers : PropTypes.array,
|
lobbyPeers : PropTypes.array,
|
||||||
|
canPromote : PropTypes.bool,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.PROMOTE_PEER);
|
||||||
room : state.room,
|
|
||||||
lobbyPeers : lobbyPeersKeySelector(state)
|
const mapStateToProps = (state) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
room : state.room,
|
||||||
|
lobbyPeers : lobbyPeersKeySelector(state),
|
||||||
|
canPromote : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
|
@ -195,19 +164,16 @@ const mapDispatchToProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.locked === next.room.locked &&
|
prev.room === next.room &&
|
||||||
prev.room.joinByAccessCode === next.room.joinByAccessCode &&
|
prev.me.roles === next.me.roles &&
|
||||||
prev.room.accessCode === next.room.accessCode &&
|
prev.peers === next.peers &&
|
||||||
prev.room.code === next.room.code &&
|
|
||||||
prev.room.lockDialogOpen === next.room.lockDialogOpen &&
|
|
||||||
prev.room.codeHidden === next.room.codeHidden &&
|
|
||||||
prev.lobbyPeers === next.lobbyPeers
|
prev.lobbyPeers === next.lobbyPeers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../RoomContext';
|
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
import randomString from 'random-string';
|
import randomString from 'random-string';
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
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 Typography from '@material-ui/core/Typography';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
|
||||||
import CookieConsent from 'react-cookie-consent';
|
import CookieConsent from 'react-cookie-consent';
|
||||||
import MuiDialogTitle from '@material-ui/core/DialogTitle';
|
import MuiDialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import MuiDialogContent from '@material-ui/core/DialogContent';
|
import MuiDialogContent from '@material-ui/core/DialogContent';
|
||||||
|
|
@ -88,63 +82,12 @@ const styles = (theme) =>
|
||||||
|
|
||||||
const DialogTitle = withStyles(styles)((props) =>
|
const DialogTitle = withStyles(styles)((props) =>
|
||||||
{
|
{
|
||||||
const [ open, setOpen ] = useState(false);
|
const { children, classes, ...other } = props;
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
|
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
|
||||||
{ window.config && window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
||||||
<Typography variant='h5'>{children}</Typography>
|
<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>
|
</MuiDialogTitle>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -165,9 +108,6 @@ const DialogActions = withStyles((theme) => ({
|
||||||
}))(MuiDialogActions);
|
}))(MuiDialogActions);
|
||||||
|
|
||||||
const ChooseRoom = ({
|
const ChooseRoom = ({
|
||||||
roomClient,
|
|
||||||
loggedIn,
|
|
||||||
myPicture,
|
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
|
|
@ -184,14 +124,8 @@ const ChooseRoom = ({
|
||||||
paper : classes.dialogPaper
|
paper : classes.dialogPaper
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle
|
<DialogTitle>
|
||||||
myPicture={myPicture}
|
{ window.config.title ? window.config.title : 'Multiparty meeting' }
|
||||||
onLogin={() =>
|
|
||||||
{
|
|
||||||
loggedIn ? roomClient.logout() : roomClient.login();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
|
|
||||||
<hr />
|
<hr />
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|
@ -244,7 +178,8 @@ const ChooseRoom = ({
|
||||||
<CookieConsent buttonText={intl.formatMessage({
|
<CookieConsent buttonText={intl.formatMessage({
|
||||||
id : 'room.consentUnderstand',
|
id : 'room.consentUnderstand',
|
||||||
defaultMessage : 'I understand'
|
defaultMessage : 'I understand'
|
||||||
})}>
|
})}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='room.cookieConsent'
|
id='room.cookieConsent'
|
||||||
defaultMessage='This website uses cookies to enhance the user experience'
|
defaultMessage='This website uses cookies to enhance the user experience'
|
||||||
|
|
@ -258,34 +193,7 @@ const ChooseRoom = ({
|
||||||
|
|
||||||
ChooseRoom.propTypes =
|
ChooseRoom.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
classes : PropTypes.object.isRequired
|
||||||
loginEnabled : PropTypes.bool.isRequired,
|
|
||||||
loggedIn : PropTypes.bool.isRequired,
|
|
||||||
myPicture : PropTypes.string,
|
|
||||||
classes : PropTypes.object.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
export default withStyles(styles)(ChooseRoom);
|
||||||
{
|
|
||||||
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)));
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { meProducersSelector } from '../Selectors';
|
import {
|
||||||
|
meProducersSelector,
|
||||||
|
makePermissionSelector
|
||||||
|
} from '../Selectors';
|
||||||
|
import { permissions } from '../../permissions';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
import { withRoomContext } from '../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
|
|
@ -11,6 +14,7 @@ import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
import VideoView from '../VideoContainers/VideoView';
|
import VideoView from '../VideoContainers/VideoView';
|
||||||
import Volume from './Volume';
|
import Volume from './Volume';
|
||||||
import Fab from '@material-ui/core/Fab';
|
import Fab from '@material-ui/core/Fab';
|
||||||
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import MicIcon from '@material-ui/icons/Mic';
|
import MicIcon from '@material-ui/icons/Mic';
|
||||||
import MicOffIcon from '@material-ui/icons/MicOff';
|
import MicOffIcon from '@material-ui/icons/MicOff';
|
||||||
|
|
@ -60,42 +64,92 @@ const styles = (theme) =>
|
||||||
margin : theme.spacing(1),
|
margin : theme.spacing(1),
|
||||||
pointerEvents : 'auto'
|
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 :
|
viewContainer :
|
||||||
{
|
{
|
||||||
position : 'relative',
|
position : 'relative',
|
||||||
width : '100%',
|
width : '100%',
|
||||||
height : '100%'
|
height : '100%'
|
||||||
},
|
},
|
||||||
controls :
|
meTag :
|
||||||
{
|
{
|
||||||
position : 'absolute',
|
position : 'absolute',
|
||||||
width : '100%',
|
float : 'left',
|
||||||
height : '100%',
|
top : '50%',
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.3)',
|
left : '50%',
|
||||||
display : 'flex',
|
transform : 'translate(-50%, -50%)',
|
||||||
flexDirection : 'column',
|
color : 'rgba(255, 255, 255, 0.5)',
|
||||||
justifyContent : 'center',
|
fontSize : '7em',
|
||||||
alignItems : 'flex-end',
|
zIndex : 30,
|
||||||
padding : theme.spacing(1),
|
margin : 0,
|
||||||
zIndex : 21,
|
opacity : 0,
|
||||||
opacity : 0,
|
transition : 'opacity 0.1s ease-in-out',
|
||||||
transition : 'opacity 0.3s',
|
'&.hover' :
|
||||||
touchAction : 'none',
|
|
||||||
pointerEvents : 'none',
|
|
||||||
'&.hover' :
|
|
||||||
{
|
{
|
||||||
opacity : 1
|
opacity : 1
|
||||||
},
|
},
|
||||||
'& p' :
|
'&.smallContainer' :
|
||||||
{
|
{
|
||||||
position : 'absolute',
|
fontSize : '3em'
|
||||||
float : 'left',
|
}
|
||||||
top : '50%',
|
},
|
||||||
left : '50%',
|
controls :
|
||||||
transform : 'translate(-50%, -50%)',
|
{
|
||||||
color : 'rgba(255, 255, 255, 0.5)',
|
position : 'absolute',
|
||||||
fontSize : '7em',
|
width : '100%',
|
||||||
margin : 0
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -115,13 +169,16 @@ const Me = (props) =>
|
||||||
activeSpeaker,
|
activeSpeaker,
|
||||||
spacing,
|
spacing,
|
||||||
style,
|
style,
|
||||||
smallButtons,
|
smallContainer,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
micProducer,
|
micProducer,
|
||||||
webcamProducer,
|
webcamProducer,
|
||||||
screenProducer,
|
screenProducer,
|
||||||
classes,
|
extraVideoProducers,
|
||||||
theme
|
canShareScreen,
|
||||||
|
transports,
|
||||||
|
noiseVolume,
|
||||||
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const videoVisible = (
|
const videoVisible = (
|
||||||
|
|
@ -230,13 +287,66 @@ const Me = (props) =>
|
||||||
defaultMessage : 'Start screen sharing'
|
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 =
|
const spacingStyle =
|
||||||
{
|
{
|
||||||
'margin' : spacing
|
'margin' : spacing
|
||||||
};
|
};
|
||||||
|
|
||||||
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
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 ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|
@ -270,9 +380,313 @@ const Me = (props) =>
|
||||||
}}
|
}}
|
||||||
style={spacingStyle}
|
style={spacingStyle}
|
||||||
>
|
>
|
||||||
<div className={classnames(classes.viewContainer)} style={style}>
|
|
||||||
<div
|
{ me.browser.platform !== 'mobile' && smallContainer &&
|
||||||
className={classnames(classes.controls, hover ? 'hover' : null)}
|
<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.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);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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 }}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<MicOffIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'device.muteAudio',
|
||||||
|
defaultMessage : 'Mute audio'
|
||||||
|
})}
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={!me.canSendMic || me.audioInProgress}
|
||||||
|
color={micState === 'on' ?
|
||||||
|
settings.voiceActivatedUnmute && !me.isAutoMuted? 'primary'
|
||||||
|
: 'default'
|
||||||
|
: 'secondary'}
|
||||||
|
size='large'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
if (micState === 'off')
|
||||||
|
roomClient.updateMic({ start: true });
|
||||||
|
else if (micState === 'on')
|
||||||
|
roomClient.muteMic();
|
||||||
|
else
|
||||||
|
roomClient.unmuteMic();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ micState === 'on' ?
|
||||||
|
<MicIcon
|
||||||
|
color={me.isAutoMuted && settings.voiceActivatedUnmute ?
|
||||||
|
'secondary' : 'primary'}
|
||||||
|
style={me.isAutoMuted && settings.voiceActivatedUnmute ?
|
||||||
|
{ opacity: noiseVolume }
|
||||||
|
: { opacity: 1 }}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<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>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<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='large'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
webcamState === 'on' ?
|
||||||
|
roomClient.disableWebcam() :
|
||||||
|
roomClient.updateWebcam({ start: true });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ webcamState === 'on' ?
|
||||||
|
<VideoIcon />
|
||||||
|
:
|
||||||
|
<VideoOffIcon />
|
||||||
|
}
|
||||||
|
</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>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'device.startScreenSharing',
|
||||||
|
defaultMessage : 'Start screen sharing'
|
||||||
|
})}
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={
|
||||||
|
!canShareScreen ||
|
||||||
|
!me.canShareScreen ||
|
||||||
|
me.screenShareInProgress
|
||||||
|
}
|
||||||
|
color={screenState === 'on' ? 'primary' : 'default'}
|
||||||
|
size='large'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
if (screenState === 'off')
|
||||||
|
roomClient.updateScreenSharing({ start: true });
|
||||||
|
else if (screenState === 'on')
|
||||||
|
roomClient.disableScreenSharing();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ (screenState === 'on' || screenState === 'unsupported') &&
|
||||||
|
<ScreenOffIcon/>
|
||||||
|
}
|
||||||
|
{ screenState === 'off' &&
|
||||||
|
<ScreenIcon/>
|
||||||
|
}
|
||||||
|
</Fab>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<VideoView
|
||||||
|
isMe
|
||||||
|
VideoView
|
||||||
|
advancedMode={advancedMode}
|
||||||
|
peer={me}
|
||||||
|
displayName={settings.displayName}
|
||||||
|
showPeerInfo
|
||||||
|
videoTrack={webcamProducer && webcamProducer.track}
|
||||||
|
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} /> }
|
||||||
|
</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)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -292,133 +706,112 @@ const Me = (props) =>
|
||||||
setHover(false);
|
setHover(false);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}}
|
}}
|
||||||
|
style={spacingStyle}
|
||||||
>
|
>
|
||||||
<p>
|
<div className={classes.viewContainer} style={style}>
|
||||||
<FormattedMessage
|
<p className={
|
||||||
id='room.me'
|
classnames(
|
||||||
defaultMessage='ME'
|
classes.meTag,
|
||||||
/>
|
hover ? 'hover' : null,
|
||||||
</p>
|
smallContainer ? 'smallContainer' : null
|
||||||
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
|
)}
|
||||||
<div>
|
>
|
||||||
<Fab
|
<FormattedMessage
|
||||||
aria-label={intl.formatMessage({
|
id='room.me'
|
||||||
id : 'device.muteAudio',
|
defaultMessage='ME'
|
||||||
defaultMessage : 'Mute audio'
|
/>
|
||||||
})}
|
</p>
|
||||||
className={classes.fab}
|
<div
|
||||||
disabled={!me.canSendMic || me.audioInProgress}
|
className={classnames(
|
||||||
color={micState === 'on' ? 'default' : 'secondary'}
|
classes.controls,
|
||||||
size={smallButtons ? 'small' : 'large'}
|
settings.hiddenControls ? 'hide' : null,
|
||||||
onClick={() =>
|
hover ? 'hover' : null
|
||||||
{
|
)}
|
||||||
if (micState === 'off')
|
onMouseOver={() => setHover(true)}
|
||||||
roomClient.enableMic();
|
onMouseOut={() => setHover(false)}
|
||||||
else if (micState === 'on')
|
onTouchStart={() =>
|
||||||
roomClient.muteMic();
|
{
|
||||||
else
|
if (touchTimeout)
|
||||||
roomClient.unmuteMic();
|
clearTimeout(touchTimeout);
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ micState === 'on' ?
|
|
||||||
<MicIcon />
|
|
||||||
:
|
|
||||||
<MicOffIcon />
|
|
||||||
}
|
|
||||||
</Fab>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title={webcamTip} placement={smallScreen ? 'top' : 'left'}>
|
|
||||||
<div>
|
|
||||||
<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={smallButtons ? 'small' : 'large'}
|
|
||||||
onClick={() =>
|
|
||||||
{
|
|
||||||
webcamState === 'on' ?
|
|
||||||
roomClient.disableWebcam() :
|
|
||||||
roomClient.enableWebcam();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ webcamState === 'on' ?
|
|
||||||
<VideoIcon />
|
|
||||||
:
|
|
||||||
<VideoOffIcon />
|
|
||||||
}
|
|
||||||
</Fab>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'left'}>
|
|
||||||
<div>
|
|
||||||
<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={smallButtons ? 'small' : 'large'}
|
|
||||||
onClick={() =>
|
|
||||||
{
|
|
||||||
switch (screenState)
|
|
||||||
{
|
|
||||||
case 'on':
|
|
||||||
{
|
|
||||||
roomClient.disableScreenSharing();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'off':
|
|
||||||
{
|
|
||||||
roomClient.enableScreenSharing();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ (screenState === 'on' || screenState === 'unsupported') &&
|
|
||||||
<ScreenOffIcon/>
|
|
||||||
}
|
|
||||||
{ screenState === 'off' &&
|
|
||||||
<ScreenIcon/>
|
|
||||||
}
|
|
||||||
</Fab>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<VideoView
|
setHover(true);
|
||||||
isMe
|
}}
|
||||||
advancedMode={advancedMode}
|
onTouchEnd={() =>
|
||||||
peer={me}
|
{
|
||||||
displayName={settings.displayName}
|
if (touchTimeout)
|
||||||
showPeerInfo
|
clearTimeout(touchTimeout);
|
||||||
videoTrack={webcamProducer && webcamProducer.track}
|
|
||||||
videoVisible={videoVisible}
|
touchTimeout = setTimeout(() =>
|
||||||
audioCodec={micProducer && micProducer.codec}
|
{
|
||||||
videoCodec={webcamProducer && webcamProducer.codec}
|
setHover(false);
|
||||||
onChangeDisplayName={(displayName) =>
|
}, 2000);
|
||||||
{
|
}}
|
||||||
roomClient.changeDisplayName(displayName);
|
>
|
||||||
}}
|
<Tooltip title={webcamTip} placement='left'>
|
||||||
>
|
{ smallContainer ?
|
||||||
<Volume id={me.id} />
|
<div>
|
||||||
</VideoView>
|
<IconButton
|
||||||
</div>
|
aria-label={intl.formatMessage({
|
||||||
</div>
|
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 &&
|
{ screenProducer &&
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.root, 'screen', hover && 'hover')}
|
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -440,37 +833,19 @@ const Me = (props) =>
|
||||||
}}
|
}}
|
||||||
style={spacingStyle}
|
style={spacingStyle}
|
||||||
>
|
>
|
||||||
<div className={classnames(classes.viewContainer)} style={style}>
|
<div className={classes.viewContainer} style={style}>
|
||||||
<div
|
<p className={
|
||||||
className={classnames(classes.controls, hover && 'hover')}
|
classnames(
|
||||||
onMouseOver={() => setHover(true)}
|
classes.meTag,
|
||||||
onMouseOut={() => setHover(false)}
|
hover ? 'hover' : null,
|
||||||
onTouchStart={() =>
|
smallContainer ? 'smallContainer' : null
|
||||||
{
|
)}
|
||||||
if (touchTimeout)
|
|
||||||
clearTimeout(touchTimeout);
|
|
||||||
|
|
||||||
setHover(true);
|
|
||||||
}}
|
|
||||||
onTouchEnd={() =>
|
|
||||||
{
|
|
||||||
|
|
||||||
if (touchTimeout)
|
|
||||||
clearTimeout(touchTimeout);
|
|
||||||
|
|
||||||
touchTimeout = setTimeout(() =>
|
|
||||||
{
|
|
||||||
setHover(false);
|
|
||||||
}, 2000);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<p>
|
<FormattedMessage
|
||||||
<FormattedMessage
|
id='room.me'
|
||||||
id='room.me'
|
defaultMessage='ME'
|
||||||
defaultMessage='ME'
|
/>
|
||||||
/>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<VideoView
|
<VideoView
|
||||||
isMe
|
isMe
|
||||||
|
|
@ -490,43 +865,74 @@ const Me = (props) =>
|
||||||
|
|
||||||
Me.propTypes =
|
Me.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
me : appPropTypes.Me.isRequired,
|
me : appPropTypes.Me.isRequired,
|
||||||
settings : PropTypes.object,
|
settings : PropTypes.object,
|
||||||
activeSpeaker : PropTypes.bool,
|
activeSpeaker : PropTypes.bool,
|
||||||
micProducer : appPropTypes.Producer,
|
micProducer : appPropTypes.Producer,
|
||||||
webcamProducer : appPropTypes.Producer,
|
webcamProducer : appPropTypes.Producer,
|
||||||
screenProducer : appPropTypes.Producer,
|
screenProducer : appPropTypes.Producer,
|
||||||
spacing : PropTypes.number,
|
extraVideoProducers : PropTypes.arrayOf(appPropTypes.Producer),
|
||||||
style : PropTypes.object,
|
spacing : PropTypes.number,
|
||||||
smallButtons : PropTypes.bool,
|
style : PropTypes.object,
|
||||||
classes : PropTypes.object.isRequired,
|
smallContainer : PropTypes.bool,
|
||||||
theme : PropTypes.object.isRequired
|
canShareScreen : PropTypes.bool.isRequired,
|
||||||
|
noiseVolume : PropTypes.number,
|
||||||
|
classes : PropTypes.object.isRequired,
|
||||||
|
theme : PropTypes.object.isRequired,
|
||||||
|
transports : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.SHARE_SCREEN);
|
||||||
me : state.me,
|
|
||||||
...meProducersSelector(state),
|
const mapStateToProps = (state) =>
|
||||||
settings : state.settings,
|
{
|
||||||
activeSpeaker : state.me.id === state.room.activeSpeakerId
|
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
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
|
prev.room === next.room &&
|
||||||
prev.me === next.me &&
|
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.producers === next.producers &&
|
||||||
prev.settings === next.settings &&
|
prev.settings === next.settings &&
|
||||||
prev.room.activeSpeakerId === next.room.activeSpeakerId
|
prev.transports === next.transports
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
import VideoView from '../VideoContainers/VideoView';
|
import VideoView from '../VideoContainers/VideoView';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import Fab from '@material-ui/core/Fab';
|
import Fab from '@material-ui/core/Fab';
|
||||||
import MicIcon from '@material-ui/icons/Mic';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import MicOffIcon from '@material-ui/icons/MicOff';
|
import VolumeUpIcon from '@material-ui/icons/VolumeUp';
|
||||||
|
import VolumeOffIcon from '@material-ui/icons/VolumeOff';
|
||||||
import NewWindowIcon from '@material-ui/icons/OpenInNew';
|
import NewWindowIcon from '@material-ui/icons/OpenInNew';
|
||||||
import FullScreenIcon from '@material-ui/icons/Fullscreen';
|
import FullScreenIcon from '@material-ui/icons/Fullscreen';
|
||||||
import Volume from './Volume';
|
import Volume from './Volume';
|
||||||
|
|
@ -59,6 +60,19 @@ const styles = (theme) =>
|
||||||
{
|
{
|
||||||
margin : theme.spacing(1)
|
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 :
|
viewContainer :
|
||||||
{
|
{
|
||||||
position : 'relative',
|
position : 'relative',
|
||||||
|
|
@ -121,14 +135,16 @@ const Peer = (props) =>
|
||||||
advancedMode,
|
advancedMode,
|
||||||
peer,
|
peer,
|
||||||
activeSpeaker,
|
activeSpeaker,
|
||||||
|
browser,
|
||||||
micConsumer,
|
micConsumer,
|
||||||
webcamConsumer,
|
webcamConsumer,
|
||||||
screenConsumer,
|
screenConsumer,
|
||||||
|
extraVideoConsumers,
|
||||||
toggleConsumerFullscreen,
|
toggleConsumerFullscreen,
|
||||||
toggleConsumerWindow,
|
toggleConsumerWindow,
|
||||||
spacing,
|
spacing,
|
||||||
style,
|
style,
|
||||||
smallButtons,
|
smallContainer,
|
||||||
windowConsumer,
|
windowConsumer,
|
||||||
classes,
|
classes,
|
||||||
theme
|
theme
|
||||||
|
|
@ -167,8 +183,8 @@ const Peer = (props) =>
|
||||||
classnames(
|
classnames(
|
||||||
classes.root,
|
classes.root,
|
||||||
'webcam',
|
'webcam',
|
||||||
hover && 'hover',
|
hover ? 'hover' : null,
|
||||||
activeSpeaker && 'active-speaker'
|
activeSpeaker ? 'active-speaker' : null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
|
|
@ -205,7 +221,7 @@ const Peer = (props) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.controls, hover && 'hover')}
|
className={classnames(classes.controls, hover ? 'hover' : null)}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -233,16 +249,16 @@ const Peer = (props) =>
|
||||||
})}
|
})}
|
||||||
placement={smallScreen ? 'top' : 'left'}
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<div>
|
{ smallContainer ?
|
||||||
<Fab
|
<IconButton
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'device.muteAudio',
|
id : 'device.muteAudio',
|
||||||
defaultMessage : 'Mute audio'
|
defaultMessage : 'Mute audio'
|
||||||
})}
|
})}
|
||||||
className={classes.fab}
|
className={classes.smallContainer}
|
||||||
disabled={!micConsumer}
|
disabled={!micConsumer}
|
||||||
color={micEnabled ? 'default' : 'secondary'}
|
color='primary'
|
||||||
size={smallButtons ? 'small' : 'large'}
|
size='small'
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
micEnabled ?
|
micEnabled ?
|
||||||
|
|
@ -251,15 +267,38 @@ const Peer = (props) =>
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ micEnabled ?
|
{ micEnabled ?
|
||||||
<MicIcon />
|
<VolumeUpIcon />
|
||||||
:
|
:
|
||||||
<MicOffIcon />
|
<VolumeOffIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
:
|
||||||
|
<Fab
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'device.muteAudio',
|
||||||
|
defaultMessage : 'Mute audio'
|
||||||
|
})}
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={!micConsumer}
|
||||||
|
color={micEnabled ? 'default' : 'secondary'}
|
||||||
|
size='large'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
micEnabled ?
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
||||||
|
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ micEnabled ?
|
||||||
|
<VolumeUpIcon />
|
||||||
|
:
|
||||||
|
<VolumeOffIcon />
|
||||||
}
|
}
|
||||||
</Fab>
|
</Fab>
|
||||||
</div>
|
}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{ !smallScreen &&
|
{ browser.platform !== 'mobile' &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id : 'label.newWindow',
|
id : 'label.newWindow',
|
||||||
|
|
@ -267,7 +306,27 @@ const Peer = (props) =>
|
||||||
})}
|
})}
|
||||||
placement={smallScreen ? 'top' : 'left'}
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<div>
|
{ 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>
|
||||||
|
:
|
||||||
<Fab
|
<Fab
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'label.newWindow',
|
id : 'label.newWindow',
|
||||||
|
|
@ -278,7 +337,7 @@ const Peer = (props) =>
|
||||||
!videoVisible ||
|
!videoVisible ||
|
||||||
(windowConsumer === webcamConsumer.id)
|
(windowConsumer === webcamConsumer.id)
|
||||||
}
|
}
|
||||||
size={smallButtons ? 'small' : 'large'}
|
size='large'
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
toggleConsumerWindow(webcamConsumer);
|
toggleConsumerWindow(webcamConsumer);
|
||||||
|
|
@ -286,7 +345,7 @@ const Peer = (props) =>
|
||||||
>
|
>
|
||||||
<NewWindowIcon />
|
<NewWindowIcon />
|
||||||
</Fab>
|
</Fab>
|
||||||
</div>
|
}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,7 +356,24 @@ const Peer = (props) =>
|
||||||
})}
|
})}
|
||||||
placement={smallScreen ? 'top' : 'left'}
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<div>
|
{ 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>
|
||||||
|
:
|
||||||
<Fab
|
<Fab
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'label.fullscreen',
|
id : 'label.fullscreen',
|
||||||
|
|
@ -305,7 +381,7 @@ const Peer = (props) =>
|
||||||
})}
|
})}
|
||||||
className={classes.fab}
|
className={classes.fab}
|
||||||
disabled={!videoVisible}
|
disabled={!videoVisible}
|
||||||
size={smallButtons ? 'small' : 'large'}
|
size='large'
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
toggleConsumerFullscreen(webcamConsumer);
|
toggleConsumerFullscreen(webcamConsumer);
|
||||||
|
|
@ -313,11 +389,12 @@ const Peer = (props) =>
|
||||||
>
|
>
|
||||||
<FullScreenIcon />
|
<FullScreenIcon />
|
||||||
</Fab>
|
</Fab>
|
||||||
</div>
|
}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VideoView
|
<VideoView
|
||||||
|
showQuality
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
peer={peer}
|
peer={peer}
|
||||||
displayName={peer.displayName}
|
displayName={peer.displayName}
|
||||||
|
|
@ -339,6 +416,7 @@ const Peer = (props) =>
|
||||||
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
|
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
|
||||||
videoTrack={webcamConsumer && webcamConsumer.track}
|
videoTrack={webcamConsumer && webcamConsumer.track}
|
||||||
videoVisible={videoVisible}
|
videoVisible={videoVisible}
|
||||||
|
audioTrack={micConsumer && micConsumer.track}
|
||||||
audioCodec={micConsumer && micConsumer.codec}
|
audioCodec={micConsumer && micConsumer.codec}
|
||||||
videoCodec={webcamConsumer && webcamConsumer.codec}
|
videoCodec={webcamConsumer && webcamConsumer.codec}
|
||||||
audioScore={micConsumer ? micConsumer.score : null}
|
audioScore={micConsumer ? micConsumer.score : null}
|
||||||
|
|
@ -349,9 +427,202 @@ const Peer = (props) =>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{ extraVideoConsumers.map((consumer) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<div key={consumer.id}
|
||||||
|
className={
|
||||||
|
classnames(
|
||||||
|
classes.root,
|
||||||
|
'webcam',
|
||||||
|
hover ? 'hover' : null,
|
||||||
|
activeSpeaker ? 'active-speaker' : null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onTouchStart={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
setHover(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
touchTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setHover(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
style={rootStyle}
|
||||||
|
>
|
||||||
|
<div className={classnames(classes.viewContainer)}>
|
||||||
|
{ !videoVisible &&
|
||||||
|
<div className={classes.videoInfo}>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.videoPaused'
|
||||||
|
defaultMessage='This video is paused'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={classnames(classes.controls, hover ? 'hover' : null)}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onTouchStart={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
setHover(true);
|
||||||
|
}}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
{
|
||||||
|
if (touchTimeout)
|
||||||
|
clearTimeout(touchTimeout);
|
||||||
|
|
||||||
|
touchTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setHover(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ browser.platform !== 'mobile' &&
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id : 'label.newWindow',
|
||||||
|
defaultMessage : 'New window'
|
||||||
|
})}
|
||||||
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
|
>
|
||||||
|
{ 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 &&
|
{ screenConsumer &&
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.root, 'screen', hover && 'hover')}
|
className={classnames(classes.root, 'screen', hover ? 'hover' : null)}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -385,7 +656,7 @@ const Peer = (props) =>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.controls, hover && 'hover')}
|
className={classnames(classes.controls, hover ? 'hover' : null)}
|
||||||
onMouseOver={() => setHover(true)}
|
onMouseOver={() => setHover(true)}
|
||||||
onMouseOut={() => setHover(false)}
|
onMouseOut={() => setHover(false)}
|
||||||
onTouchStart={() =>
|
onTouchStart={() =>
|
||||||
|
|
@ -407,7 +678,7 @@ const Peer = (props) =>
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ !smallScreen &&
|
{ browser.platform !== 'mobile' &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id : 'label.newWindow',
|
id : 'label.newWindow',
|
||||||
|
|
@ -415,26 +686,24 @@ const Peer = (props) =>
|
||||||
})}
|
})}
|
||||||
placement={smallScreen ? 'top' : 'left'}
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<div>
|
<Fab
|
||||||
<Fab
|
aria-label={intl.formatMessage({
|
||||||
aria-label={intl.formatMessage({
|
id : 'label.newWindow',
|
||||||
id : 'label.newWindow',
|
defaultMessage : 'New window'
|
||||||
defaultMessage : 'New window'
|
})}
|
||||||
})}
|
className={classes.fab}
|
||||||
className={classes.fab}
|
disabled={
|
||||||
disabled={
|
!screenVisible ||
|
||||||
!screenVisible ||
|
(windowConsumer === screenConsumer.id)
|
||||||
(windowConsumer === screenConsumer.id)
|
}
|
||||||
}
|
size={smallContainer ? 'small' : 'large'}
|
||||||
size={smallButtons ? 'small' : 'large'}
|
onClick={() =>
|
||||||
onClick={() =>
|
{
|
||||||
{
|
toggleConsumerWindow(screenConsumer);
|
||||||
toggleConsumerWindow(screenConsumer);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<NewWindowIcon />
|
||||||
<NewWindowIcon />
|
</Fab>
|
||||||
</Fab>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,26 +714,25 @@ const Peer = (props) =>
|
||||||
})}
|
})}
|
||||||
placement={smallScreen ? 'top' : 'left'}
|
placement={smallScreen ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<div>
|
<Fab
|
||||||
<Fab
|
aria-label={intl.formatMessage({
|
||||||
aria-label={intl.formatMessage({
|
id : 'label.fullscreen',
|
||||||
id : 'label.fullscreen',
|
defaultMessage : 'Fullscreen'
|
||||||
defaultMessage : 'Fullscreen'
|
})}
|
||||||
})}
|
className={classes.fab}
|
||||||
className={classes.fab}
|
disabled={!screenVisible}
|
||||||
disabled={!screenVisible}
|
size={smallContainer ? 'small' : 'large'}
|
||||||
size={smallButtons ? 'small' : 'large'}
|
onClick={() =>
|
||||||
onClick={() =>
|
{
|
||||||
{
|
toggleConsumerFullscreen(screenConsumer);
|
||||||
toggleConsumerFullscreen(screenConsumer);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<FullScreenIcon />
|
||||||
<FullScreenIcon />
|
</Fab>
|
||||||
</Fab>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<VideoView
|
<VideoView
|
||||||
|
showQuality
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoContain
|
videoContain
|
||||||
consumerSpatialLayers={
|
consumerSpatialLayers={
|
||||||
|
|
@ -489,6 +757,7 @@ const Peer = (props) =>
|
||||||
videoTrack={screenConsumer && screenConsumer.track}
|
videoTrack={screenConsumer && screenConsumer.track}
|
||||||
videoVisible={screenVisible}
|
videoVisible={screenVisible}
|
||||||
videoCodec={screenConsumer && screenConsumer.codec}
|
videoCodec={screenConsumer && screenConsumer.codec}
|
||||||
|
videoScore={screenConsumer ? screenConsumer.score : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -505,11 +774,13 @@ Peer.propTypes =
|
||||||
micConsumer : appPropTypes.Consumer,
|
micConsumer : appPropTypes.Consumer,
|
||||||
webcamConsumer : appPropTypes.Consumer,
|
webcamConsumer : appPropTypes.Consumer,
|
||||||
screenConsumer : appPropTypes.Consumer,
|
screenConsumer : appPropTypes.Consumer,
|
||||||
|
extraVideoConsumers : PropTypes.arrayOf(appPropTypes.Consumer),
|
||||||
windowConsumer : PropTypes.string,
|
windowConsumer : PropTypes.string,
|
||||||
activeSpeaker : PropTypes.bool,
|
activeSpeaker : PropTypes.bool,
|
||||||
|
browser : PropTypes.object.isRequired,
|
||||||
spacing : PropTypes.number,
|
spacing : PropTypes.number,
|
||||||
style : PropTypes.object,
|
style : PropTypes.object,
|
||||||
smallButtons : PropTypes.bool,
|
smallContainer : PropTypes.bool,
|
||||||
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
||||||
toggleConsumerWindow : PropTypes.func.isRequired,
|
toggleConsumerWindow : PropTypes.func.isRequired,
|
||||||
classes : PropTypes.object.isRequired,
|
classes : PropTypes.object.isRequired,
|
||||||
|
|
@ -526,7 +797,8 @@ const makeMapStateToProps = (initialState, { id }) =>
|
||||||
peer : state.peers[id],
|
peer : state.peers[id],
|
||||||
...getPeerConsumers(state, id),
|
...getPeerConsumers(state, id),
|
||||||
windowConsumer : state.room.windowConsumer,
|
windowConsumer : state.room.windowConsumer,
|
||||||
activeSpeaker : id === state.room.activeSpeakerId
|
activeSpeaker : id === state.room.activeSpeakerId,
|
||||||
|
browser : state.me.browser
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -560,7 +832,8 @@ export default withRoomContext(connect(
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||||
prev.room.windowConsumer === next.room.windowConsumer
|
prev.room.windowConsumer === next.room.windowConsumer &&
|
||||||
|
prev.me.browser === next.me.browser
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,16 +91,6 @@ const SpeakerPeer = (props) =>
|
||||||
!screenConsumer.remotelyPaused
|
!screenConsumer.remotelyPaused
|
||||||
);
|
);
|
||||||
|
|
||||||
let videoProfile;
|
|
||||||
|
|
||||||
if (webcamConsumer)
|
|
||||||
videoProfile = webcamConsumer.profile;
|
|
||||||
|
|
||||||
let screenProfile;
|
|
||||||
|
|
||||||
if (screenConsumer)
|
|
||||||
screenProfile = screenConsumer.profile;
|
|
||||||
|
|
||||||
const spacingStyle =
|
const spacingStyle =
|
||||||
{
|
{
|
||||||
'margin' : spacing
|
'margin' : spacing
|
||||||
|
|
@ -134,11 +124,27 @@ const SpeakerPeer = (props) =>
|
||||||
peer={peer}
|
peer={peer}
|
||||||
displayName={peer.displayName}
|
displayName={peer.displayName}
|
||||||
showPeerInfo
|
showPeerInfo
|
||||||
videoTrack={webcamConsumer ? webcamConsumer.track : null}
|
consumerSpatialLayers={webcamConsumer ? webcamConsumer.spatialLayers : null}
|
||||||
|
consumerTemporalLayers={webcamConsumer ? webcamConsumer.temporalLayers : null}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
webcamConsumer ? webcamConsumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
webcamConsumer ? webcamConsumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
webcamConsumer ? webcamConsumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
webcamConsumer ? webcamConsumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
|
||||||
|
videoTrack={webcamConsumer && webcamConsumer.track}
|
||||||
videoVisible={videoVisible}
|
videoVisible={videoVisible}
|
||||||
videoProfile={videoProfile}
|
audioCodec={micConsumer && micConsumer.codec}
|
||||||
audioCodec={micConsumer ? micConsumer.codec : null}
|
videoCodec={webcamConsumer && webcamConsumer.codec}
|
||||||
videoCodec={webcamConsumer ? webcamConsumer.codec : null}
|
audioScore={micConsumer ? micConsumer.score : null}
|
||||||
|
videoScore={webcamConsumer ? webcamConsumer.score : null}
|
||||||
>
|
>
|
||||||
<Volume id={peer.id} />
|
<Volume id={peer.id} />
|
||||||
</VideoView>
|
</VideoView>
|
||||||
|
|
@ -165,10 +171,29 @@ const SpeakerPeer = (props) =>
|
||||||
<VideoView
|
<VideoView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoContain
|
videoContain
|
||||||
videoTrack={screenConsumer ? screenConsumer.track : null}
|
consumerSpatialLayers={
|
||||||
|
screenConsumer ? screenConsumer.spatialLayers : null
|
||||||
|
}
|
||||||
|
consumerTemporalLayers={
|
||||||
|
screenConsumer ? screenConsumer.temporalLayers : null
|
||||||
|
}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
screenConsumer ? screenConsumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
screenConsumer ? screenConsumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
screenConsumer ? screenConsumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
screenConsumer ? screenConsumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={screenConsumer && screenConsumer.type !== 'simple'}
|
||||||
|
videoTrack={screenConsumer && screenConsumer.track}
|
||||||
videoVisible={screenVisible}
|
videoVisible={screenVisible}
|
||||||
videoProfile={screenProfile}
|
videoCodec={screenConsumer && screenConsumer.codec}
|
||||||
videoCodec={screenConsumer ? screenConsumer.codec : null}
|
videoScore={screenConsumer ? screenConsumer.score : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,27 +58,27 @@ const styles = () =>
|
||||||
'&.level6' :
|
'&.level6' :
|
||||||
{
|
{
|
||||||
height : '60%',
|
height : '60%',
|
||||||
backgroundColor : 'rgba(255, 0, 0, 0.65)'
|
backgroundColor : 'rgba(255, 165, 0, 0.65)'
|
||||||
},
|
},
|
||||||
'&.level7' :
|
'&.level7' :
|
||||||
{
|
{
|
||||||
height : '70%',
|
height : '70%',
|
||||||
backgroundColor : 'rgba(255, 0, 0, 0.65)'
|
backgroundColor : 'rgba(255, 100, 0, 0.65)'
|
||||||
},
|
},
|
||||||
'&.level8' :
|
'&.level8' :
|
||||||
{
|
{
|
||||||
height : '80%',
|
height : '80%',
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.65)'
|
backgroundColor : 'rgba(255, 60, 0, 0.65)'
|
||||||
},
|
},
|
||||||
'&.level9' :
|
'&.level9' :
|
||||||
{
|
{
|
||||||
height : '90%',
|
height : '90%',
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.65)'
|
backgroundColor : 'rgba(255, 30, 0, 0.65)'
|
||||||
},
|
},
|
||||||
'&.level10' :
|
'&.level10' :
|
||||||
{
|
{
|
||||||
height : '100%',
|
height : '100%',
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.65)'
|
backgroundColor : 'rgba(255, 0, 0, 0.65)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
volumeSmall :
|
volumeSmall :
|
||||||
|
|
@ -94,17 +94,17 @@ const styles = () =>
|
||||||
smallBar :
|
smallBar :
|
||||||
{
|
{
|
||||||
flex : '0 0 auto',
|
flex : '0 0 auto',
|
||||||
margin : '0.3rem',
|
|
||||||
backgroundSize : '75%',
|
backgroundSize : '75%',
|
||||||
backgroundRepeat : 'no-repeat',
|
backgroundRepeat : 'no-repeat',
|
||||||
backgroundColor : 'rgba(0, 0, 0, 1)',
|
backgroundColor : 'rgba(0, 0, 0, 1)',
|
||||||
cursor : 'pointer',
|
cursor : 'pointer',
|
||||||
transitionProperty : 'opacity, background-color',
|
transitionProperty : 'opacity, background-color',
|
||||||
width : 3,
|
width : 3,
|
||||||
borderRadius : 6,
|
borderRadius : 2,
|
||||||
transitionDuration : '0.25s',
|
transitionDuration : '0.25s',
|
||||||
position : 'absolute',
|
position : 'absolute',
|
||||||
bottom : 0,
|
top : '50%',
|
||||||
|
transform : 'translateY(-50%)',
|
||||||
'&.level0' : { height: 0 },
|
'&.level0' : { height: 0 },
|
||||||
'&.level1' : { height: '0.2vh' },
|
'&.level1' : { height: '0.2vh' },
|
||||||
'&.level2' : { height: '0.4vh' },
|
'&.level2' : { height: '0.4vh' },
|
||||||
|
|
@ -149,9 +149,16 @@ const makeMapStateToProps = (initialState, props) =>
|
||||||
{
|
{
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
if (state.peerVolumes[props.id]>state.settings.noiseThreshold)
|
||||||
volume : state.peerVolumes[props.id]
|
{
|
||||||
};
|
return {
|
||||||
|
volume : Math.round((state.peerVolumes[props.id]+100) / 10)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return { volume: 0 };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return mapStateToProps;
|
return mapStateToProps;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
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 Union’s Horizon 2020 research and innovation
|
||||||
|
programme under Grant Agreement No. 731122 (GN4-2).
|
||||||
|
On behalf of GÉANT project, GÉANT Association is the sole
|
||||||
|
owner of the copyright in all material which was developed
|
||||||
|
by a member of the GÉANT project.
|
||||||
|
</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)));
|
||||||
|
|
@ -0,0 +1,341 @@
|
||||||
|
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)));
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import { withRoomContext } from '../../RoomContext';
|
||||||
|
import * as roomActions from '../../actions/roomActions';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
import MenuItem from '@material-ui/core/MenuItem';
|
||||||
|
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||||
|
import FormControl from '@material-ui/core/FormControl';
|
||||||
|
import Select from '@material-ui/core/Select';
|
||||||
|
|
||||||
|
const styles = (theme) =>
|
||||||
|
({
|
||||||
|
dialogPaper :
|
||||||
|
{
|
||||||
|
width : '30vw',
|
||||||
|
[theme.breakpoints.down('lg')] :
|
||||||
|
{
|
||||||
|
width : '40vw'
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('md')] :
|
||||||
|
{
|
||||||
|
width : '50vw'
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('sm')] :
|
||||||
|
{
|
||||||
|
width : '70vw'
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('xs')] :
|
||||||
|
{
|
||||||
|
width : '90vw'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setting :
|
||||||
|
{
|
||||||
|
padding : theme.spacing(2)
|
||||||
|
},
|
||||||
|
formControl :
|
||||||
|
{
|
||||||
|
display : 'flex'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ExtraVideo = ({
|
||||||
|
roomClient,
|
||||||
|
extraVideoOpen,
|
||||||
|
webcamDevices,
|
||||||
|
handleCloseExtraVideo,
|
||||||
|
classes
|
||||||
|
}) =>
|
||||||
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const [ videoDevice, setVideoDevice ] = React.useState('');
|
||||||
|
|
||||||
|
const handleChange = (event) =>
|
||||||
|
{
|
||||||
|
setVideoDevice(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
let videoDevices;
|
||||||
|
|
||||||
|
if (webcamDevices)
|
||||||
|
videoDevices = Object.values(webcamDevices);
|
||||||
|
else
|
||||||
|
videoDevices = [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={extraVideoOpen}
|
||||||
|
onClose={() => handleCloseExtraVideo(false)}
|
||||||
|
classes={{
|
||||||
|
paper : classes.dialogPaper
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle id='form-dialog-title'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.extraVideo'
|
||||||
|
defaultMessage='Extra video'
|
||||||
|
/>
|
||||||
|
</DialogTitle>
|
||||||
|
<form className={classes.setting} autoComplete='off'>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={videoDevice}
|
||||||
|
displayEmpty
|
||||||
|
name={intl.formatMessage({
|
||||||
|
id : 'settings.camera',
|
||||||
|
defaultMessage : 'Camera'
|
||||||
|
})}
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
disabled={videoDevices.length === 0}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{ videoDevices.map((webcam, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{ videoDevices.length > 0 ?
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.selectCamera',
|
||||||
|
defaultMessage : 'Select video device'
|
||||||
|
})
|
||||||
|
:
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.cantSelectCamera',
|
||||||
|
defaultMessage : 'Unable to select video device'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => roomClient.addExtraVideo(videoDevice)} color='primary'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='label.addVideo'
|
||||||
|
defaultMessage='Add video'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ExtraVideo.propTypes =
|
||||||
|
{
|
||||||
|
roomClient : PropTypes.object.isRequired,
|
||||||
|
extraVideoOpen : PropTypes.bool.isRequired,
|
||||||
|
webcamDevices : PropTypes.object,
|
||||||
|
handleCloseExtraVideo : PropTypes.func.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) =>
|
||||||
|
({
|
||||||
|
webcamDevices : state.me.webcamDevices,
|
||||||
|
extraVideoOpen : state.room.extraVideoOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
handleCloseExtraVideo : roomActions.setExtraVideoOpen
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withRoomContext(connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
areStatesEqual : (next, prev) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
prev.me.webcamDevices === next.me.webcamDevices &&
|
||||||
|
prev.room.extraVideoOpen === next.room.extraVideoOpen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)(withStyles(styles)(ExtraVideo)));
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import { withRoomContext } from '../../RoomContext';
|
||||||
|
import * as roomActions from '../../actions/roomActions';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
import 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)));
|
||||||
|
|
@ -1,23 +1,32 @@
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
lobbyPeersKeySelector,
|
lobbyPeersKeySelector,
|
||||||
peersLengthSelector
|
peersLengthSelector,
|
||||||
|
raisedHandsSelector,
|
||||||
|
makePermissionSelector
|
||||||
} from '../Selectors';
|
} from '../Selectors';
|
||||||
|
import { permissions } from '../../permissions';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
import { withRoomContext } from '../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import * as roomActions from '../../actions/roomActions';
|
import * as roomActions from '../../actions/roomActions';
|
||||||
import * as toolareaActions from '../../actions/toolareaActions';
|
import * as toolareaActions from '../../actions/toolareaActions';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import classnames from 'classnames';
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
|
import MenuItem from '@material-ui/core/MenuItem';
|
||||||
|
import Menu from '@material-ui/core/Menu';
|
||||||
|
import Popover from '@material-ui/core/Popover';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import MenuIcon from '@material-ui/icons/Menu';
|
import MenuIcon from '@material-ui/icons/Menu';
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
import Badge from '@material-ui/core/Badge';
|
import Badge from '@material-ui/core/Badge';
|
||||||
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import ExtensionIcon from '@material-ui/icons/Extension';
|
||||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||||
import FullScreenIcon from '@material-ui/icons/Fullscreen';
|
import FullScreenIcon from '@material-ui/icons/Fullscreen';
|
||||||
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
||||||
|
|
@ -26,11 +35,40 @@ import SecurityIcon from '@material-ui/icons/Security';
|
||||||
import PeopleIcon from '@material-ui/icons/People';
|
import PeopleIcon from '@material-ui/icons/People';
|
||||||
import LockIcon from '@material-ui/icons/Lock';
|
import LockIcon from '@material-ui/icons/Lock';
|
||||||
import LockOpenIcon from '@material-ui/icons/LockOpen';
|
import LockOpenIcon from '@material-ui/icons/LockOpen';
|
||||||
|
import VideoCallIcon from '@material-ui/icons/VideoCall';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
import MoreIcon from '@material-ui/icons/MoreVert';
|
||||||
|
import HelpIcon from '@material-ui/icons/Help';
|
||||||
|
import InfoIcon from '@material-ui/icons/Info';
|
||||||
|
|
||||||
const styles = (theme) =>
|
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 :
|
menuButton :
|
||||||
{
|
{
|
||||||
margin : 0,
|
margin : 0,
|
||||||
|
|
@ -47,7 +85,7 @@ const styles = (theme) =>
|
||||||
},
|
},
|
||||||
divider :
|
divider :
|
||||||
{
|
{
|
||||||
marginLeft : theme.spacing(3),
|
marginLeft : theme.spacing(3)
|
||||||
},
|
},
|
||||||
show :
|
show :
|
||||||
{
|
{
|
||||||
|
|
@ -72,14 +110,34 @@ const styles = (theme) =>
|
||||||
display : 'block'
|
display : 'block'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actionButtons :
|
sectionDesktop : {
|
||||||
{
|
display : 'none',
|
||||||
display : 'flex'
|
[theme.breakpoints.up('md')] : {
|
||||||
|
display : 'flex'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sectionMobile : {
|
||||||
|
display : 'flex',
|
||||||
|
[theme.breakpoints.up('md')] : {
|
||||||
|
display : 'none'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actionButton :
|
actionButton :
|
||||||
{
|
{
|
||||||
margin : theme.spacing(1, 0),
|
margin : theme.spacing(1, 0),
|
||||||
padding : theme.spacing(0, 1)
|
padding : theme.spacing(0, 1)
|
||||||
|
},
|
||||||
|
disabledButton :
|
||||||
|
{
|
||||||
|
margin : theme.spacing(1, 0)
|
||||||
|
},
|
||||||
|
green :
|
||||||
|
{
|
||||||
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
|
},
|
||||||
|
moreAction :
|
||||||
|
{
|
||||||
|
margin : theme.spacing(0.5, 0, 0.5, 1.5)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -118,12 +176,47 @@ const TopBar = (props) =>
|
||||||
{
|
{
|
||||||
const intl = useIntl();
|
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 {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
room,
|
room,
|
||||||
peersLength,
|
peersLength,
|
||||||
lobbyPeers,
|
lobbyPeers,
|
||||||
permanentTopBar,
|
permanentTopBar,
|
||||||
|
drawerOverlayed,
|
||||||
|
toolAreaOpen,
|
||||||
|
isMobile,
|
||||||
myPicture,
|
myPicture,
|
||||||
loggedIn,
|
loggedIn,
|
||||||
loginEnabled,
|
loginEnabled,
|
||||||
|
|
@ -131,13 +224,22 @@ const TopBar = (props) =>
|
||||||
fullscreen,
|
fullscreen,
|
||||||
onFullscreen,
|
onFullscreen,
|
||||||
setSettingsOpen,
|
setSettingsOpen,
|
||||||
|
setExtraVideoOpen,
|
||||||
|
setHelpOpen,
|
||||||
|
setAboutOpen,
|
||||||
setLockDialogOpen,
|
setLockDialogOpen,
|
||||||
toggleToolArea,
|
toggleToolArea,
|
||||||
openUsersTab,
|
openUsersTab,
|
||||||
unread,
|
unread,
|
||||||
|
canProduceExtraVideo,
|
||||||
|
canLock,
|
||||||
|
canPromote,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const isMenuOpen = Boolean(anchorEl);
|
||||||
|
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
|
||||||
|
|
||||||
const lockTooltip = room.locked ?
|
const lockTooltip = room.locked ?
|
||||||
intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'tooltip.unLockRoom',
|
id : 'tooltip.unLockRoom',
|
||||||
|
|
@ -172,170 +274,240 @@ const TopBar = (props) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar
|
<React.Fragment>
|
||||||
position='fixed'
|
<AppBar
|
||||||
className={room.toolbarsVisible || permanentTopBar ? classes.show : classes.hide}
|
position='fixed'
|
||||||
>
|
className={classnames(
|
||||||
<Toolbar>
|
room.toolbarsVisible || permanentTopBar ?
|
||||||
<PulsingBadge
|
classes.show : classes.hide,
|
||||||
color='secondary'
|
!(isMobile || drawerOverlayed) && toolAreaOpen ?
|
||||||
badgeContent={unread}
|
classes.persistentDrawerOpen : null
|
||||||
onClick={() => toggleToolArea()}
|
)}
|
||||||
>
|
>
|
||||||
<IconButton
|
<Toolbar>
|
||||||
color='inherit'
|
<PulsingBadge
|
||||||
aria-label={intl.formatMessage({
|
color='secondary'
|
||||||
id : 'label.openDrawer',
|
badgeContent={unread}
|
||||||
defaultMessage : 'Open drawer'
|
onClick={() => toggleToolArea()}
|
||||||
})}
|
|
||||||
className={classes.menuButton}
|
|
||||||
>
|
|
||||||
<MenuIcon />
|
|
||||||
</IconButton>
|
|
||||||
</PulsingBadge>
|
|
||||||
{ 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 && window.config.title ? window.config.title : 'Multiparty meeting' }
|
|
||||||
</Typography>
|
|
||||||
<div className={classes.grow} />
|
|
||||||
<div className={classes.actionButtons}>
|
|
||||||
{ fullscreenEnabled &&
|
|
||||||
<Tooltip title={fullscreenTooltip}>
|
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.enterFullscreen',
|
|
||||||
defaultMessage : 'Enter fullscreen'
|
|
||||||
})}
|
|
||||||
className={classes.actionButton}
|
|
||||||
color='inherit'
|
|
||||||
onClick={onFullscreen}
|
|
||||||
>
|
|
||||||
{ fullscreen ?
|
|
||||||
<FullScreenExitIcon />
|
|
||||||
:
|
|
||||||
<FullScreenIcon />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
<Tooltip
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id : 'tooltip.participants',
|
|
||||||
defaultMessage : 'Show participants'
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
color='inherit'
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'label.openDrawer',
|
||||||
|
defaultMessage : 'Open drawer'
|
||||||
|
})}
|
||||||
|
className={classes.menuButton}
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
</PulsingBadge>
|
||||||
|
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
||||||
|
<Typography
|
||||||
|
className={classes.title}
|
||||||
|
variant='h6'
|
||||||
|
color='inherit'
|
||||||
|
noWrap
|
||||||
|
>
|
||||||
|
{ window.config.title ? window.config.title : 'Multiparty meeting' }
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.grow} />
|
||||||
|
<div className={classes.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>
|
||||||
|
{ fullscreenEnabled &&
|
||||||
|
<Tooltip title={fullscreenTooltip}>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.enterFullscreen',
|
||||||
|
defaultMessage : 'Enter fullscreen'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
onClick={onFullscreen}
|
||||||
|
>
|
||||||
|
{ fullscreen ?
|
||||||
|
<FullScreenExitIcon />
|
||||||
|
:
|
||||||
|
<FullScreenIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
id : 'tooltip.participants',
|
id : 'tooltip.participants',
|
||||||
defaultMessage : 'Show participants'
|
defaultMessage : 'Show participants'
|
||||||
})}
|
})}
|
||||||
color='inherit'
|
|
||||||
onClick={() => openUsersTab()}
|
|
||||||
>
|
>
|
||||||
<Badge
|
<IconButton
|
||||||
color='primary'
|
aria-label={intl.formatMessage({
|
||||||
badgeContent={peersLength + 1}
|
id : 'tooltip.participants',
|
||||||
|
defaultMessage : 'Show participants'
|
||||||
|
})}
|
||||||
|
color='inherit'
|
||||||
|
onClick={() => openUsersTab()}
|
||||||
>
|
>
|
||||||
<PeopleIcon />
|
<Badge
|
||||||
</Badge>
|
color='primary'
|
||||||
</IconButton>
|
badgeContent={peersLength + 1}
|
||||||
</Tooltip>
|
>
|
||||||
<Tooltip
|
<PeopleIcon />
|
||||||
title={intl.formatMessage({
|
</Badge>
|
||||||
id : 'tooltip.settings',
|
</IconButton>
|
||||||
defaultMessage : 'Show settings'
|
</Tooltip>
|
||||||
})}
|
<Tooltip
|
||||||
>
|
title={intl.formatMessage({
|
||||||
<IconButton
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id : 'tooltip.settings',
|
id : 'tooltip.settings',
|
||||||
defaultMessage : 'Show settings'
|
defaultMessage : 'Show settings'
|
||||||
})}
|
})}
|
||||||
className={classes.actionButton}
|
|
||||||
color='inherit'
|
|
||||||
onClick={() => setSettingsOpen(!room.settingsOpen)}
|
|
||||||
>
|
>
|
||||||
<SettingsIcon />
|
<IconButton
|
||||||
</IconButton>
|
aria-label={intl.formatMessage({
|
||||||
</Tooltip>
|
id : 'tooltip.settings',
|
||||||
<Tooltip title={lockTooltip}>
|
defaultMessage : 'Show settings'
|
||||||
<IconButton
|
})}
|
||||||
aria-label={intl.formatMessage({
|
className={classes.actionButton}
|
||||||
id : 'tooltip.lockRoom',
|
color='inherit'
|
||||||
defaultMessage : 'Lock room'
|
onClick={() => setSettingsOpen(!room.settingsOpen)}
|
||||||
})}
|
>
|
||||||
className={classes.actionButton}
|
<SettingsIcon />
|
||||||
color='inherit'
|
</IconButton>
|
||||||
onClick={() =>
|
</Tooltip>
|
||||||
{
|
<Tooltip title={lockTooltip}>
|
||||||
if (room.locked)
|
<span className={classes.disabledButton}>
|
||||||
{
|
<IconButton
|
||||||
roomClient.unlockRoom();
|
aria-label={intl.formatMessage({
|
||||||
}
|
id : 'tooltip.lockRoom',
|
||||||
else
|
defaultMessage : 'Lock room'
|
||||||
{
|
})}
|
||||||
roomClient.lockRoom();
|
className={classes.actionButton}
|
||||||
}
|
color='inherit'
|
||||||
}}
|
disabled={!canLock}
|
||||||
>
|
onClick={() =>
|
||||||
{ room.locked ?
|
{
|
||||||
<LockIcon />
|
if (room.locked)
|
||||||
:
|
{
|
||||||
<LockOpenIcon />
|
roomClient.unlockRoom();
|
||||||
}
|
}
|
||||||
</IconButton>
|
else
|
||||||
</Tooltip>
|
{
|
||||||
{ lobbyPeers.length > 0 &&
|
roomClient.lockRoom();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ room.locked ?
|
||||||
|
<LockIcon />
|
||||||
|
:
|
||||||
|
<LockOpenIcon />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{ lobbyPeers.length > 0 &&
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id : 'tooltip.lobby',
|
||||||
|
defaultMessage : 'Show lobby'
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className={classes.disabledButton}>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.lobby',
|
||||||
|
defaultMessage : 'Show lobby'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
disabled={!canPromote}
|
||||||
|
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
|
||||||
|
>
|
||||||
|
<PulsingBadge
|
||||||
|
color='secondary'
|
||||||
|
badgeContent={lobbyPeers.length}
|
||||||
|
>
|
||||||
|
<SecurityIcon />
|
||||||
|
</PulsingBadge>
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
{ loginEnabled &&
|
||||||
|
<Tooltip title={loginTooltip}>
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.login',
|
||||||
|
defaultMessage : 'Log in'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
color='inherit'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
loggedIn ? roomClient.logout() : roomClient.login();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ myPicture ?
|
||||||
|
<Avatar src={myPicture} />
|
||||||
|
:
|
||||||
|
<AccountCircle className={loggedIn ? classes.green : null} />
|
||||||
|
}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className={classes.sectionMobile}>
|
||||||
|
{ lobbyPeers.length > 0 &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id : 'tooltip.lobby',
|
id : 'tooltip.lobby',
|
||||||
defaultMessage : 'Show lobby'
|
defaultMessage : 'Show lobby'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<IconButton
|
<span className={classes.disabledButton}>
|
||||||
aria-label={intl.formatMessage({
|
<IconButton
|
||||||
id : 'tooltip.lobby',
|
aria-label={intl.formatMessage({
|
||||||
defaultMessage : 'Show lobby'
|
id : 'tooltip.lobby',
|
||||||
})}
|
defaultMessage : 'Show lobby'
|
||||||
color='inherit'
|
})}
|
||||||
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
|
className={classes.actionButton}
|
||||||
>
|
color='inherit'
|
||||||
<PulsingBadge
|
disabled={!canPromote}
|
||||||
color='secondary'
|
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
|
||||||
badgeContent={lobbyPeers.length}
|
|
||||||
>
|
>
|
||||||
<SecurityIcon />
|
<PulsingBadge
|
||||||
</PulsingBadge>
|
color='secondary'
|
||||||
</IconButton>
|
badgeContent={lobbyPeers.length}
|
||||||
|
>
|
||||||
|
<SecurityIcon />
|
||||||
|
</PulsingBadge>
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{ loginEnabled &&
|
<IconButton
|
||||||
<Tooltip title={loginTooltip}>
|
aria-haspopup
|
||||||
<IconButton
|
onClick={handleMobileMenuOpen}
|
||||||
aria-label={intl.formatMessage({
|
color='inherit'
|
||||||
id : 'tooltip.login',
|
>
|
||||||
defaultMessage : 'Log in'
|
<MoreIcon />
|
||||||
})}
|
</IconButton>
|
||||||
className={classes.actionButton}
|
</div>
|
||||||
color='inherit'
|
|
||||||
onClick={() =>
|
|
||||||
{
|
|
||||||
loggedIn ? roomClient.logout() : roomClient.login();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ myPicture ?
|
|
||||||
<Avatar src={myPicture} />
|
|
||||||
:
|
|
||||||
<AccountCircle />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
<div className={classes.divider} />
|
<div className={classes.divider} />
|
||||||
<Button
|
<Button
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
|
|
@ -352,47 +524,312 @@ const TopBar = (props) =>
|
||||||
defaultMessage='Leave'
|
defaultMessage='Leave'
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</Toolbar>
|
||||||
</Toolbar>
|
</AppBar>
|
||||||
</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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
TopBar.propTypes =
|
TopBar.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.object.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
peersLength : PropTypes.number,
|
isMobile : PropTypes.bool.isRequired,
|
||||||
lobbyPeers : PropTypes.array,
|
peersLength : PropTypes.number,
|
||||||
permanentTopBar : PropTypes.bool,
|
lobbyPeers : PropTypes.array,
|
||||||
myPicture : PropTypes.string,
|
permanentTopBar : PropTypes.bool.isRequired,
|
||||||
loggedIn : PropTypes.bool.isRequired,
|
drawerOverlayed : PropTypes.bool.isRequired,
|
||||||
loginEnabled : PropTypes.bool.isRequired,
|
toolAreaOpen : PropTypes.bool.isRequired,
|
||||||
fullscreenEnabled : PropTypes.bool,
|
myPicture : PropTypes.string,
|
||||||
fullscreen : PropTypes.bool,
|
loggedIn : PropTypes.bool.isRequired,
|
||||||
onFullscreen : PropTypes.func.isRequired,
|
loginEnabled : PropTypes.bool.isRequired,
|
||||||
setToolbarsVisible : PropTypes.func.isRequired,
|
fullscreenEnabled : PropTypes.bool,
|
||||||
setSettingsOpen : PropTypes.func.isRequired,
|
fullscreen : PropTypes.bool,
|
||||||
setLockDialogOpen : PropTypes.func.isRequired,
|
onFullscreen : PropTypes.func.isRequired,
|
||||||
toggleToolArea : PropTypes.func.isRequired,
|
setToolbarsVisible : PropTypes.func.isRequired,
|
||||||
openUsersTab : PropTypes.func.isRequired,
|
setSettingsOpen : PropTypes.func.isRequired,
|
||||||
unread : PropTypes.number.isRequired,
|
setExtraVideoOpen : PropTypes.func.isRequired,
|
||||||
classes : PropTypes.object.isRequired,
|
setHelpOpen : PropTypes.func.isRequired,
|
||||||
theme : PropTypes.object.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 mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
({
|
{
|
||||||
room : state.room,
|
const hasExtraVideoPermission =
|
||||||
peersLength : peersLengthSelector(state),
|
makePermissionSelector(permissions.EXTRA_VIDEO);
|
||||||
lobbyPeers : lobbyPeersKeySelector(state),
|
|
||||||
permanentTopBar : state.settings.permanentTopBar,
|
const hasLockPermission =
|
||||||
loggedIn : state.me.loggedIn,
|
makePermissionSelector(permissions.CHANGE_ROOM_LOCK);
|
||||||
loginEnabled : state.me.loginEnabled,
|
|
||||||
myPicture : state.me.picture,
|
const hasPromotionPermission =
|
||||||
unread : state.toolarea.unreadMessages +
|
makePermissionSelector(permissions.PROMOTE_PEER);
|
||||||
state.toolarea.unreadFiles
|
|
||||||
});
|
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)
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
({
|
({
|
||||||
|
|
@ -402,11 +839,23 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
},
|
},
|
||||||
setSettingsOpen : (settingsOpen) =>
|
setSettingsOpen : (settingsOpen) =>
|
||||||
{
|
{
|
||||||
dispatch(roomActions.setSettingsOpen({ settingsOpen }));
|
dispatch(roomActions.setSettingsOpen(settingsOpen));
|
||||||
|
},
|
||||||
|
setExtraVideoOpen : (extraVideoOpen) =>
|
||||||
|
{
|
||||||
|
dispatch(roomActions.setExtraVideoOpen(extraVideoOpen));
|
||||||
|
},
|
||||||
|
setHelpOpen : (helpOpen) =>
|
||||||
|
{
|
||||||
|
dispatch(roomActions.setHelpOpen(helpOpen));
|
||||||
|
},
|
||||||
|
setAboutOpen : (aboutOpen) =>
|
||||||
|
{
|
||||||
|
dispatch(roomActions.setAboutOpen(aboutOpen));
|
||||||
},
|
},
|
||||||
setLockDialogOpen : (lockDialogOpen) =>
|
setLockDialogOpen : (lockDialogOpen) =>
|
||||||
{
|
{
|
||||||
dispatch(roomActions.setLockDialogOpen({ lockDialogOpen }));
|
dispatch(roomActions.setLockDialogOpen(lockDialogOpen));
|
||||||
},
|
},
|
||||||
toggleToolArea : () =>
|
toggleToolArea : () =>
|
||||||
{
|
{
|
||||||
|
|
@ -420,7 +869,7 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
|
|
@ -431,11 +880,15 @@ export default withRoomContext(connect(
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.lobbyPeers === next.lobbyPeers &&
|
prev.lobbyPeers === next.lobbyPeers &&
|
||||||
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
|
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
|
||||||
|
prev.settings.drawerOverlayed === next.settings.drawerOverlayed &&
|
||||||
prev.me.loggedIn === next.me.loggedIn &&
|
prev.me.loggedIn === next.me.loggedIn &&
|
||||||
|
prev.me.browser === next.me.browser &&
|
||||||
prev.me.loginEnabled === next.me.loginEnabled &&
|
prev.me.loginEnabled === next.me.loginEnabled &&
|
||||||
prev.me.picture === next.me.picture &&
|
prev.me.picture === next.me.picture &&
|
||||||
|
prev.me.roles === next.me.roles &&
|
||||||
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
||||||
prev.toolarea.unreadFiles === next.toolarea.unreadFiles
|
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
|
||||||
|
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../RoomContext';
|
import { withRoomContext } from '../RoomContext';
|
||||||
|
import classnames from 'classnames';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import * as settingsActions from '../actions/settingsActions';
|
import * as settingsActions from '../actions/settingsActions';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
@ -82,6 +83,10 @@ const styles = (theme) =>
|
||||||
green :
|
green :
|
||||||
{
|
{
|
||||||
color : 'rgba(0, 153, 0, 1)'
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
|
},
|
||||||
|
red :
|
||||||
|
{
|
||||||
|
color : 'rgba(153, 0, 0, 1)'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -103,7 +108,7 @@ const DialogTitle = withStyles(styles)((props) =>
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { children, classes, myPicture, onLogin, ...other } = props;
|
const { children, classes, myPicture, onLogin, loggedIn, ...other } = props;
|
||||||
|
|
||||||
const handleTooltipClose = () =>
|
const handleTooltipClose = () =>
|
||||||
{
|
{
|
||||||
|
|
@ -115,19 +120,27 @@ const DialogTitle = withStyles(styles)((props) =>
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loginTooltip = loggedIn ?
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'tooltip.logout',
|
||||||
|
defaultMessage : 'Log out'
|
||||||
|
})
|
||||||
|
:
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'tooltip.login',
|
||||||
|
defaultMessage : 'Log in'
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
|
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
|
||||||
{ window.config && window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
||||||
<Typography variant='h5'>{children}</Typography>
|
<Typography variant='h5'>{children}</Typography>
|
||||||
{ window.config && window.config.loginEnabled &&
|
{ window.config.loginEnabled &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
onClose={handleTooltipClose}
|
onClose={handleTooltipClose}
|
||||||
onOpen={handleTooltipOpen}
|
onOpen={handleTooltipOpen}
|
||||||
open={open}
|
open={open}
|
||||||
title={intl.formatMessage({
|
title={loginTooltip}
|
||||||
id : 'tooltip.login',
|
|
||||||
defaultMessage : 'Click to log in'
|
|
||||||
})}
|
|
||||||
placement='left'
|
placement='left'
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|
@ -139,7 +152,9 @@ const DialogTitle = withStyles(styles)((props) =>
|
||||||
{ myPicture ?
|
{ myPicture ?
|
||||||
<Avatar src={myPicture} className={classes.largeAvatar} />
|
<Avatar src={myPicture} className={classes.largeAvatar} />
|
||||||
:
|
:
|
||||||
<AccountCircle className={classes.largeIcon} />
|
<AccountCircle
|
||||||
|
className={classnames(classes.largeIcon, loggedIn ? classes.green : null)}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
@ -209,10 +224,11 @@ const JoinDialog = ({
|
||||||
myPicture={myPicture}
|
myPicture={myPicture}
|
||||||
onLogin={() =>
|
onLogin={() =>
|
||||||
{
|
{
|
||||||
loggedIn ? roomClient.logout() : roomClient.login();
|
loggedIn ? roomClient.logout(roomId) : roomClient.login(roomId);
|
||||||
}}
|
}}
|
||||||
|
loggedIn={loggedIn}
|
||||||
>
|
>
|
||||||
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
|
{ window.config.title ? window.config.title : 'Multiparty meeting' }
|
||||||
<hr />
|
<hr />
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|
@ -269,6 +285,16 @@ const JoinDialog = ({
|
||||||
}}
|
}}
|
||||||
fullWidth
|
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>
|
</DialogContent>
|
||||||
|
|
||||||
|
|
@ -307,6 +333,7 @@ const JoinDialog = ({
|
||||||
className={classes.green}
|
className={classes.green}
|
||||||
gutterBottom
|
gutterBottom
|
||||||
variant='h6'
|
variant='h6'
|
||||||
|
style={{ fontWeight: '600' }}
|
||||||
align='center'
|
align='center'
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|
@ -315,7 +342,11 @@ const JoinDialog = ({
|
||||||
/>
|
/>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
{ room.signInRequired ?
|
{ room.signInRequired ?
|
||||||
<DialogContentText gutterBottom>
|
<DialogContentText
|
||||||
|
gutterBottom
|
||||||
|
variant='h5'
|
||||||
|
style={{ fontWeight: '600' }}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='room.emptyRequireLogin'
|
id='room.emptyRequireLogin'
|
||||||
defaultMessage={
|
defaultMessage={
|
||||||
|
|
@ -325,7 +356,11 @@ const JoinDialog = ({
|
||||||
/>
|
/>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
:
|
:
|
||||||
<DialogContentText gutterBottom>
|
<DialogContentText
|
||||||
|
gutterBottom
|
||||||
|
variant='h5'
|
||||||
|
style={{ fontWeight: '600' }}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='room.locketWait'
|
id='room.locketWait'
|
||||||
defaultMessage='The room is locked - hang on until somebody lets you in ...'
|
defaultMessage='The room is locked - hang on until somebody lets you in ...'
|
||||||
|
|
@ -339,7 +374,8 @@ const JoinDialog = ({
|
||||||
<CookieConsent buttonText={intl.formatMessage({
|
<CookieConsent buttonText={intl.formatMessage({
|
||||||
id : 'room.consentUnderstand',
|
id : 'room.consentUnderstand',
|
||||||
defaultMessage : 'I understand'
|
defaultMessage : 'I understand'
|
||||||
})}>
|
})}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='room.cookieConsent'
|
id='room.cookieConsent'
|
||||||
defaultMessage='This website uses cookies to enhance the user experience'
|
defaultMessage='This website uses cookies to enhance the user experience'
|
||||||
|
|
@ -397,6 +433,7 @@ export default withRoomContext(connect(
|
||||||
return (
|
return (
|
||||||
prev.room.inLobby === next.room.inLobby &&
|
prev.room.inLobby === next.room.inLobby &&
|
||||||
prev.room.signInRequired === next.room.signInRequired &&
|
prev.room.signInRequired === next.room.signInRequired &&
|
||||||
|
prev.room.overRoomLimit === next.room.overRoomLimit &&
|
||||||
prev.settings.displayName === next.settings.displayName &&
|
prev.settings.displayName === next.settings.displayName &&
|
||||||
prev.me.displayNameInProgress === next.me.displayNameInProgress &&
|
prev.me.displayNameInProgress === next.me.displayNameInProgress &&
|
||||||
prev.me.loginEnabled === next.me.loginEnabled &&
|
prev.me.loginEnabled === next.me.loginEnabled &&
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import ChatModerator from './ChatModerator';
|
||||||
import MessageList from './MessageList';
|
import MessageList from './MessageList';
|
||||||
import ChatInput from './ChatInput';
|
import ChatInput from './ChatInput';
|
||||||
|
|
||||||
|
|
@ -25,6 +26,7 @@ const Chat = (props) =>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper className={classes.root}>
|
<Paper className={classes.root}>
|
||||||
|
<ChatModerator />
|
||||||
<MessageList />
|
<MessageList />
|
||||||
<ChatInput />
|
<ChatInput />
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import InputBase from '@material-ui/core/InputBase';
|
import InputBase from '@material-ui/core/InputBase';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
|
@ -54,6 +56,7 @@ const ChatInput = (props) =>
|
||||||
roomClient,
|
roomClient,
|
||||||
displayName,
|
displayName,
|
||||||
picture,
|
picture,
|
||||||
|
canChat,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
|
@ -66,6 +69,7 @@ const ChatInput = (props) =>
|
||||||
defaultMessage : 'Enter chat message...'
|
defaultMessage : 'Enter chat message...'
|
||||||
})}
|
})}
|
||||||
value={message || ''}
|
value={message || ''}
|
||||||
|
disabled={!canChat}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyPress={(ev) =>
|
onKeyPress={(ev) =>
|
||||||
{
|
{
|
||||||
|
|
@ -89,6 +93,7 @@ const ChatInput = (props) =>
|
||||||
color='primary'
|
color='primary'
|
||||||
className={classes.iconButton}
|
className={classes.iconButton}
|
||||||
aria-label='Send'
|
aria-label='Send'
|
||||||
|
disabled={!canChat}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
if (message && message !== '')
|
if (message && message !== '')
|
||||||
|
|
@ -112,24 +117,36 @@ ChatInput.propTypes =
|
||||||
roomClient : PropTypes.object.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
displayName : PropTypes.string,
|
displayName : PropTypes.string,
|
||||||
picture : PropTypes.string,
|
picture : PropTypes.string,
|
||||||
|
canChat : PropTypes.bool.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
({
|
{
|
||||||
displayName : state.settings.displayName,
|
const hasPermission = makePermissionSelector(permissions.SEND_CHAT);
|
||||||
picture : state.me.picture
|
|
||||||
});
|
const mapStateToProps = (state) =>
|
||||||
|
({
|
||||||
|
displayName : state.settings.displayName,
|
||||||
|
picture : state.me.picture,
|
||||||
|
canChat : hasPermission(state)
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
export default withRoomContext(
|
export default withRoomContext(
|
||||||
connect(
|
connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
|
prev.room === next.room &&
|
||||||
|
prev.me.roles === next.me.roles &&
|
||||||
|
prev.peers === next.peers &&
|
||||||
prev.settings.displayName === next.settings.displayName &&
|
prev.settings.displayName === next.settings.displayName &&
|
||||||
prev.me.picture === next.me.picture
|
prev.me.picture === next.me.picture
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
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)));
|
||||||
|
|
@ -6,6 +6,7 @@ import DOMPurify from 'dompurify';
|
||||||
import marked from 'marked';
|
import marked from 'marked';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
const linkRenderer = new marked.Renderer();
|
const linkRenderer = new marked.Renderer();
|
||||||
|
|
||||||
|
|
@ -55,6 +56,8 @@ const styles = (theme) =>
|
||||||
|
|
||||||
const Message = (props) =>
|
const Message = (props) =>
|
||||||
{
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
self,
|
self,
|
||||||
picture,
|
picture,
|
||||||
|
|
@ -88,7 +91,16 @@ const Message = (props) =>
|
||||||
}
|
}
|
||||||
) }}
|
) }}
|
||||||
/>
|
/>
|
||||||
<Typography variant='caption'>{self ? 'Me' : name} - {time}</Typography>
|
<Typography variant='caption'>
|
||||||
|
{ self ?
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'room.me',
|
||||||
|
defaultMessage : 'Me'
|
||||||
|
})
|
||||||
|
:
|
||||||
|
name
|
||||||
|
} - {time}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,10 @@ import { connect } from 'react-redux';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import FileList from './FileList';
|
import FileList from './FileList';
|
||||||
|
import FileSharingModerator from './FileSharingModerator';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
|
|
@ -24,6 +27,10 @@ const styles = (theme) =>
|
||||||
button :
|
button :
|
||||||
{
|
{
|
||||||
margin : theme.spacing(1)
|
margin : theme.spacing(1)
|
||||||
|
},
|
||||||
|
shareButtonsWrapper :
|
||||||
|
{
|
||||||
|
display : 'flex'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -35,12 +42,14 @@ const FileSharing = (props) =>
|
||||||
{
|
{
|
||||||
if (event.target.files.length > 0)
|
if (event.target.files.length > 0)
|
||||||
{
|
{
|
||||||
props.roomClient.shareFiles(event.target.files);
|
await props.roomClient.shareFiles(event.target.files);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
canShareFiles,
|
canShareFiles,
|
||||||
|
browser,
|
||||||
|
canShare,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
|
@ -55,25 +64,61 @@ const FileSharing = (props) =>
|
||||||
defaultMessage : 'File sharing not supported'
|
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 (
|
return (
|
||||||
<Paper className={classes.root}>
|
<Paper className={classes.root}>
|
||||||
<input
|
<FileSharingModerator />
|
||||||
className={classes.input}
|
<div className={classes.shareButtonsWrapper} >
|
||||||
type='file'
|
<input
|
||||||
onChange={handleFileChange}
|
className={classes.input}
|
||||||
id='share-files-button'
|
type='file'
|
||||||
/>
|
disabled={!canShare}
|
||||||
<label htmlFor='share-files-button'>
|
onChange={handleFileChange}
|
||||||
<Button
|
// Need to reset to be able to share same file twice
|
||||||
variant='contained'
|
onClick={(e) => (e.target.value = null)}
|
||||||
component='span'
|
id='share-files-button'
|
||||||
className={classes.button}
|
/>
|
||||||
disabled={!canShareFiles}
|
<input
|
||||||
>
|
className={classes.input}
|
||||||
{buttonDescription}
|
type='file'
|
||||||
</Button>
|
disabled={!canShare}
|
||||||
</label>
|
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}
|
||||||
|
>
|
||||||
|
{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 />
|
<FileList />
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|
@ -81,19 +126,45 @@ const FileSharing = (props) =>
|
||||||
|
|
||||||
FileSharing.propTypes = {
|
FileSharing.propTypes = {
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
|
browser : PropTypes.object.isRequired,
|
||||||
canShareFiles : PropTypes.bool.isRequired,
|
canShareFiles : PropTypes.bool.isRequired,
|
||||||
tabOpen : PropTypes.bool.isRequired,
|
tabOpen : PropTypes.bool.isRequired,
|
||||||
|
canShare : PropTypes.bool.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.SHARE_FILE);
|
||||||
canShareFiles : state.me.canShareFiles,
|
|
||||||
tabOpen : state.toolarea.currentToolTab === 'files'
|
const mapStateToProps = (state) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
canShareFiles : state.me.canShareFiles,
|
||||||
|
browser : state.me.browser,
|
||||||
|
tabOpen : state.toolarea.currentToolTab === 'files',
|
||||||
|
canShare : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
)(withStyles(styles)(FileSharing)));
|
)(withStyles(styles)(FileSharing)));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
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)));
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { raisedHandsSelector } from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import * as toolareaActions from '../../actions/toolareaActions';
|
import * as toolareaActions from '../../actions/toolareaActions';
|
||||||
|
|
@ -51,6 +52,7 @@ const MeetingDrawer = (props) =>
|
||||||
currentToolTab,
|
currentToolTab,
|
||||||
unreadMessages,
|
unreadMessages,
|
||||||
unreadFiles,
|
unreadFiles,
|
||||||
|
raisedHands,
|
||||||
closeDrawer,
|
closeDrawer,
|
||||||
setToolTab,
|
setToolTab,
|
||||||
classes,
|
classes,
|
||||||
|
|
@ -93,10 +95,14 @@ const MeetingDrawer = (props) =>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label={intl.formatMessage({
|
label={
|
||||||
id : 'label.participants',
|
<Badge color='secondary' badgeContent={raisedHands}>
|
||||||
defaultMessage : 'Participants'
|
{intl.formatMessage({
|
||||||
})}
|
id : 'label.participants',
|
||||||
|
defaultMessage : 'Participants'
|
||||||
|
})}
|
||||||
|
</Badge>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<IconButton onClick={closeDrawer}>
|
<IconButton onClick={closeDrawer}>
|
||||||
|
|
@ -116,16 +122,21 @@ MeetingDrawer.propTypes =
|
||||||
setToolTab : PropTypes.func.isRequired,
|
setToolTab : PropTypes.func.isRequired,
|
||||||
unreadMessages : PropTypes.number.isRequired,
|
unreadMessages : PropTypes.number.isRequired,
|
||||||
unreadFiles : PropTypes.number.isRequired,
|
unreadFiles : PropTypes.number.isRequired,
|
||||||
|
raisedHands : PropTypes.number.isRequired,
|
||||||
closeDrawer : PropTypes.func.isRequired,
|
closeDrawer : PropTypes.func.isRequired,
|
||||||
classes : PropTypes.object.isRequired,
|
classes : PropTypes.object.isRequired,
|
||||||
theme : PropTypes.object.isRequired
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) =>
|
||||||
currentToolTab : state.toolarea.currentToolTab,
|
{
|
||||||
unreadMessages : state.toolarea.unreadMessages,
|
return {
|
||||||
unreadFiles : state.toolarea.unreadFiles
|
currentToolTab : state.toolarea.currentToolTab,
|
||||||
});
|
unreadMessages : state.toolarea.unreadMessages,
|
||||||
|
unreadFiles : state.toolarea.unreadFiles,
|
||||||
|
raisedHands : raisedHandsSelector(state)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setToolTab : toolareaActions.setToolTab
|
setToolTab : toolareaActions.setToolTab
|
||||||
|
|
@ -141,7 +152,8 @@ export default connect(
|
||||||
return (
|
return (
|
||||||
prev.toolarea.currentToolTab === next.toolarea.currentToolTab &&
|
prev.toolarea.currentToolTab === next.toolarea.currentToolTab &&
|
||||||
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
|
||||||
prev.toolarea.unreadFiles === next.toolarea.unreadFiles
|
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
|
||||||
|
prev.peers === next.peers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,55 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
import PanIcon from '@material-ui/icons/PanTool';
|
||||||
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
||||||
import HandIcon from '../../../images/icon-hand-white.svg';
|
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(1),
|
|
||||||
width : '100%',
|
width : '100%',
|
||||||
overflow : 'hidden',
|
overflow : 'hidden',
|
||||||
cursor : 'auto',
|
cursor : 'auto',
|
||||||
display : 'flex'
|
display : 'flex'
|
||||||
},
|
},
|
||||||
listPeer :
|
|
||||||
{
|
|
||||||
display : 'flex'
|
|
||||||
},
|
|
||||||
avatar :
|
avatar :
|
||||||
{
|
{
|
||||||
borderRadius : '50%',
|
borderRadius : '50%',
|
||||||
height : '2rem'
|
height : '2rem',
|
||||||
|
marginTop : theme.spacing(0.5)
|
||||||
},
|
},
|
||||||
peerInfo :
|
peerInfo :
|
||||||
{
|
{
|
||||||
fontSize : '1rem',
|
fontSize : '1rem',
|
||||||
border : 'none',
|
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
paddingLeft : theme.spacing(1),
|
paddingLeft : theme.spacing(1),
|
||||||
flexGrow : 1,
|
flexGrow : 1,
|
||||||
alignItems : 'center'
|
alignItems : 'center'
|
||||||
},
|
},
|
||||||
indicators :
|
buttons :
|
||||||
{
|
{
|
||||||
left : 0,
|
padding : theme.spacing(1)
|
||||||
top : 0,
|
|
||||||
display : 'flex',
|
|
||||||
flexDirection : 'row',
|
|
||||||
justifyContent : 'flex-start',
|
|
||||||
alignItems : 'center',
|
|
||||||
transition : 'opacity 0.3s'
|
|
||||||
},
|
},
|
||||||
icon :
|
green :
|
||||||
{
|
{
|
||||||
flex : '0 0 auto',
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
margin : '0.3rem',
|
|
||||||
borderRadius : 2,
|
|
||||||
backgroundPosition : 'center',
|
|
||||||
backgroundSize : '75%',
|
|
||||||
backgroundRepeat : 'no-repeat',
|
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.5)',
|
|
||||||
transitionProperty : 'opacity, background-color',
|
|
||||||
transitionDuration : '0.15s',
|
|
||||||
width : 'var(--media-control-button-size)',
|
|
||||||
height : 'var(--media-control-button-size)',
|
|
||||||
opacity : 0.85,
|
|
||||||
'&:hover' :
|
|
||||||
{
|
|
||||||
opacity : 1
|
|
||||||
},
|
|
||||||
'&.raise-hand' :
|
|
||||||
{
|
|
||||||
backgroundImage : `url(${HandIcon})`,
|
|
||||||
opacity : 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListMe = (props) =>
|
const ListMe = (props) =>
|
||||||
{
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
roomClient,
|
||||||
me,
|
me,
|
||||||
settings,
|
settings,
|
||||||
classes
|
classes
|
||||||
|
|
@ -82,29 +58,49 @@ const ListMe = (props) =>
|
||||||
const picture = me.picture || EmptyAvatar;
|
const picture = me.picture || EmptyAvatar;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.listPeer}>
|
<img alt='My avatar' className={classes.avatar} src={picture} />
|
||||||
<img alt='My avatar' className={classes.avatar} src={picture} />
|
|
||||||
|
|
||||||
<div className={classes.peerInfo}>
|
<div className={classes.peerInfo}>
|
||||||
{settings.displayName}
|
{settings.displayName}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={classes.indicators}>
|
|
||||||
{ me.raisedHand &&
|
|
||||||
<div className={classnames(classes.icon, 'raise-hand')} />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
<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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ListMe.propTypes =
|
ListMe.propTypes =
|
||||||
{
|
{
|
||||||
me : appPropTypes.Me.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
settings : PropTypes.object.isRequired,
|
me : appPropTypes.Me.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
settings : PropTypes.object.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
|
|
@ -112,7 +108,7 @@ const mapStateToProps = (state) => ({
|
||||||
settings : state.settings
|
settings : state.settings
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|
@ -125,4 +121,4 @@ export default connect(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)(withStyles(styles)(ListMe));
|
)(withStyles(styles)(ListMe)));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
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)));
|
||||||
|
|
@ -3,41 +3,43 @@ import { connect } from 'react-redux';
|
||||||
import { makePeerConsumerSelector } from '../../Selectors';
|
import { makePeerConsumerSelector } from '../../Selectors';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { green } from '@material-ui/core/colors';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
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 MicIcon from '@material-ui/icons/Mic';
|
||||||
import MicOffIcon from '@material-ui/icons/MicOff';
|
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 ScreenIcon from '@material-ui/icons/ScreenShare';
|
||||||
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
||||||
|
import ExitIcon from '@material-ui/icons/ExitToApp';
|
||||||
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
||||||
import HandIcon from '../../../images/icon-hand-white.svg';
|
import PanIcon from '@material-ui/icons/PanTool';
|
||||||
|
import RecordVoiceOverIcon from '@material-ui/icons/RecordVoiceOver';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(1),
|
|
||||||
width : '100%',
|
width : '100%',
|
||||||
overflow : 'hidden',
|
overflow : 'hidden',
|
||||||
cursor : 'auto',
|
cursor : 'auto',
|
||||||
display : 'flex'
|
display : 'flex'
|
||||||
},
|
},
|
||||||
listPeer :
|
|
||||||
{
|
|
||||||
display : 'flex'
|
|
||||||
},
|
|
||||||
avatar :
|
avatar :
|
||||||
{
|
{
|
||||||
borderRadius : '50%',
|
borderRadius : '50%',
|
||||||
height : '2rem'
|
height : '2rem',
|
||||||
|
marginTop : theme.spacing(0.5)
|
||||||
},
|
},
|
||||||
peerInfo :
|
peerInfo :
|
||||||
{
|
{
|
||||||
fontSize : '1rem',
|
fontSize : '1rem',
|
||||||
border : 'none',
|
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
paddingLeft : theme.spacing(1),
|
paddingLeft : theme.spacing(1),
|
||||||
flexGrow : 1,
|
flexGrow : 1,
|
||||||
|
|
@ -45,86 +47,17 @@ const styles = (theme) =>
|
||||||
},
|
},
|
||||||
indicators :
|
indicators :
|
||||||
{
|
{
|
||||||
left : 0,
|
display : 'flex',
|
||||||
top : 0,
|
padding : theme.spacing(1)
|
||||||
display : 'flex',
|
|
||||||
flexDirection : 'row',
|
|
||||||
justifyContent : 'flex-start',
|
|
||||||
alignItems : 'center',
|
|
||||||
transition : 'opacity 0.3s'
|
|
||||||
},
|
},
|
||||||
icon :
|
buttons :
|
||||||
{
|
{
|
||||||
flex : '0 0 auto',
|
padding : theme.spacing(1)
|
||||||
margin : '0.3rem',
|
|
||||||
borderRadius : 2,
|
|
||||||
backgroundPosition : 'center',
|
|
||||||
backgroundSize : '75%',
|
|
||||||
backgroundRepeat : 'no-repeat',
|
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.5)',
|
|
||||||
transitionProperty : 'opacity, background-color',
|
|
||||||
transitionDuration : '0.15s',
|
|
||||||
width : 'var(--media-control-button-size)',
|
|
||||||
height : 'var(--media-control-button-size)',
|
|
||||||
opacity : 0.85,
|
|
||||||
'&:hover' :
|
|
||||||
{
|
|
||||||
opacity : 1
|
|
||||||
},
|
|
||||||
'&.on' :
|
|
||||||
{
|
|
||||||
opacity : 1
|
|
||||||
},
|
|
||||||
'&.off' :
|
|
||||||
{
|
|
||||||
opacity : 0.2
|
|
||||||
},
|
|
||||||
'&.raise-hand' :
|
|
||||||
{
|
|
||||||
backgroundImage : `url(${HandIcon})`
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
controls :
|
green :
|
||||||
{
|
{
|
||||||
float : 'right',
|
color : 'rgba(0, 153, 0, 1)',
|
||||||
display : 'flex',
|
marginLeft : theme.spacing(2)
|
||||||
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)'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -134,13 +67,22 @@ const ListPeer = (props) =>
|
||||||
|
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
|
isModerator,
|
||||||
|
spotlight,
|
||||||
peer,
|
peer,
|
||||||
micConsumer,
|
micConsumer,
|
||||||
|
webcamConsumer,
|
||||||
screenConsumer,
|
screenConsumer,
|
||||||
children,
|
children,
|
||||||
classes
|
classes
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const webcamEnabled = (
|
||||||
|
Boolean(webcamConsumer) &&
|
||||||
|
!webcamConsumer.locallyPaused &&
|
||||||
|
!webcamConsumer.remotelyPaused
|
||||||
|
);
|
||||||
|
|
||||||
const micEnabled = (
|
const micEnabled = (
|
||||||
Boolean(micConsumer) &&
|
Boolean(micConsumer) &&
|
||||||
!micConsumer.locallyPaused &&
|
!micConsumer.locallyPaused &&
|
||||||
|
|
@ -162,36 +104,54 @@ const ListPeer = (props) =>
|
||||||
<div className={classes.peerInfo}>
|
<div className={classes.peerInfo}>
|
||||||
{peer.displayName}
|
{peer.displayName}
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.indicators}>
|
{ peer.raisedHand &&
|
||||||
{ peer.raiseHandState &&
|
<IconButton
|
||||||
<div className={
|
className={classes.buttons}
|
||||||
classnames(
|
style={{ color: green[500] }}
|
||||||
classes.icon, 'raise-hand', {
|
disabled={!isModerator || peer.raisedHandInProgress}
|
||||||
on : peer.raiseHandState,
|
onClick={(e) =>
|
||||||
off : !peer.raiseHandState
|
{
|
||||||
}
|
e.stopPropagation();
|
||||||
)
|
|
||||||
}
|
roomClient.lowerPeerHand(peer.id);
|
||||||
/>
|
}}
|
||||||
}
|
>
|
||||||
</div>
|
<PanIcon />
|
||||||
{children}
|
</IconButton>
|
||||||
<div className={classes.controls}>
|
}
|
||||||
{ screenConsumer &&
|
{ 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'
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'tooltip.muteScreenSharing',
|
id : 'tooltip.muteScreenSharing',
|
||||||
defaultMessage : 'Mute participant share'
|
defaultMessage : 'Mute participant share'
|
||||||
})}
|
})}
|
||||||
color={ screenVisible ? 'primary' : 'secondary'}
|
color={screenVisible ? 'primary' : 'secondary'}
|
||||||
disabled={ peer.peerScreenInProgress }
|
disabled={peer.peerScreenInProgress}
|
||||||
|
className={classes.buttons}
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
screenVisible ?
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
screenVisible ?
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
||||||
}}
|
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{ screenVisible ?
|
{ screenVisible ?
|
||||||
<ScreenIcon />
|
<ScreenIcon />
|
||||||
|
|
@ -199,29 +159,181 @@ const ListPeer = (props) =>
|
||||||
<ScreenOffIcon />
|
<ScreenOffIcon />
|
||||||
}
|
}
|
||||||
</IconButton>
|
</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
|
<IconButton
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id : 'tooltip.muteParticipant',
|
id : 'tooltip.muteParticipant',
|
||||||
defaultMessage : 'Mute participant'
|
defaultMessage : 'Mute participant'
|
||||||
})}
|
})}
|
||||||
color={ micEnabled ? 'primary' : 'secondary'}
|
color={micEnabled ? 'primary' : 'secondary'}
|
||||||
disabled={ peer.peerAudioInProgress }
|
disabled={peer.peerAudioInProgress}
|
||||||
|
className={classes.buttons}
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
micEnabled ?
|
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
micEnabled ?
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
||||||
}}
|
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{ micEnabled ?
|
{ micEnabled ?
|
||||||
<MicIcon />
|
<VolumeUpIcon />
|
||||||
:
|
:
|
||||||
<MicOffIcon />
|
<VolumeOffIcon />
|
||||||
}
|
}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -230,6 +342,8 @@ ListPeer.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
|
isModerator : PropTypes.bool,
|
||||||
|
spotlight : PropTypes.bool,
|
||||||
peer : appPropTypes.Peer.isRequired,
|
peer : appPropTypes.Peer.isRequired,
|
||||||
micConsumer : appPropTypes.Consumer,
|
micConsumer : appPropTypes.Consumer,
|
||||||
webcamConsumer : appPropTypes.Consumer,
|
webcamConsumer : appPropTypes.Consumer,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
passivePeersSelector,
|
participantListSelector,
|
||||||
spotlightPeersSelector
|
makePermissionSelector
|
||||||
} from '../../Selectors';
|
} from '../../Selectors';
|
||||||
import classNames from 'classnames';
|
import { permissions } from '../../../permissions';
|
||||||
|
import classnames from 'classnames';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Flipper, Flipped } from 'react-flip-toolkit';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import ListPeer from './ListPeer';
|
import ListPeer from './ListPeer';
|
||||||
import ListMe from './ListMe';
|
import ListMe from './ListMe';
|
||||||
|
import ListModerator from './ListModerator';
|
||||||
import Volume from '../../Containers/Volume';
|
import Volume from '../../Containers/Volume';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
|
|
@ -30,12 +33,10 @@ const styles = (theme) =>
|
||||||
},
|
},
|
||||||
listheader :
|
listheader :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(1),
|
|
||||||
fontWeight : 'bolder'
|
fontWeight : 'bolder'
|
||||||
},
|
},
|
||||||
listItem :
|
listItem :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(1),
|
|
||||||
width : '100%',
|
width : '100%',
|
||||||
overflow : 'hidden',
|
overflow : 'hidden',
|
||||||
cursor : 'pointer',
|
cursor : 'pointer',
|
||||||
|
|
@ -76,14 +77,26 @@ class ParticipantList extends React.PureComponent
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
passivePeers,
|
isModerator,
|
||||||
|
participants,
|
||||||
|
spotlights,
|
||||||
selectedPeerId,
|
selectedPeerId,
|
||||||
spotlightPeers,
|
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root} ref={(node) => { this.node = node; }}>
|
<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}>
|
<ul className={classes.list}>
|
||||||
<li className={classes.listheader}>
|
<li className={classes.listheader}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|
@ -96,42 +109,42 @@ class ParticipantList extends React.PureComponent
|
||||||
<ul className={classes.list}>
|
<ul className={classes.list}>
|
||||||
<li className={classes.listheader}>
|
<li className={classes.listheader}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='room.spotlights'
|
id='label.participants'
|
||||||
defaultMessage='Participants in Spotlight'
|
defaultMessage='Participants'
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
{ spotlightPeers.map((peerId) => (
|
<Flipper
|
||||||
<li
|
flipKey={participants}
|
||||||
key={peerId}
|
>
|
||||||
className={classNames(classes.listItem, {
|
{ participants.map((peer) => (
|
||||||
selected : peerId === selectedPeerId
|
<Flipped key={peer.id} flipId={peer.id}>
|
||||||
})}
|
<li
|
||||||
onClick={() => roomClient.setSelectedPeer(peerId)}
|
key={peer.id}
|
||||||
>
|
className={classnames(classes.listItem, {
|
||||||
<ListPeer id={peerId} advancedMode={advancedMode}>
|
selected : peer.id === selectedPeerId
|
||||||
<Volume small id={peerId} />
|
})}
|
||||||
</ListPeer>
|
onClick={() => roomClient.setSelectedPeer(peer.id)}
|
||||||
</li>
|
>
|
||||||
))}
|
{ spotlights.includes(peer.id) ?
|
||||||
</ul>
|
<ListPeer
|
||||||
<ul className={classes.list}>
|
id={peer.id}
|
||||||
<li className={classes.listheader}>
|
advancedMode={advancedMode}
|
||||||
<FormattedMessage
|
isModerator={isModerator}
|
||||||
id='room.passive'
|
spotlight
|
||||||
defaultMessage='Passive Participants'
|
>
|
||||||
/>
|
<Volume small id={peer.id} />
|
||||||
</li>
|
</ListPeer>
|
||||||
{ passivePeers.map((peerId) => (
|
:
|
||||||
<li
|
<ListPeer
|
||||||
key={peerId}
|
id={peer.id}
|
||||||
className={classNames(classes.listItem, {
|
advancedMode={advancedMode}
|
||||||
selected : peerId === selectedPeerId
|
isModerator={isModerator}
|
||||||
})}
|
/>
|
||||||
onClick={() => roomClient.setSelectedPeer(peerId)}
|
}
|
||||||
>
|
</li>
|
||||||
<ListPeer id={peerId} advancedMode={advancedMode} />
|
</Flipped>
|
||||||
</li>
|
))}
|
||||||
))}
|
</Flipper>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -142,32 +155,41 @@ ParticipantList.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
passivePeers : PropTypes.array,
|
isModerator : PropTypes.bool,
|
||||||
|
participants : PropTypes.array,
|
||||||
|
spotlights : PropTypes.array,
|
||||||
selectedPeerId : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlightPeers : PropTypes.array,
|
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.MODERATE_ROOM);
|
||||||
passivePeers : passivePeersSelector(state),
|
|
||||||
selectedPeerId : state.room.selectedPeerId,
|
const mapStateToProps = (state) =>
|
||||||
spotlightPeers : spotlightPeersSelector(state)
|
{
|
||||||
|
return {
|
||||||
|
isModerator : hasPermission(state),
|
||||||
|
participants : participantListSelector(state),
|
||||||
|
spotlights : state.room.spotlights,
|
||||||
|
selectedPeerId : state.room.selectedPeerId
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParticipantListContainer = withRoomContext(connect(
|
const ParticipantListContainer = withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.peers === next.peers &&
|
prev.room === next.room &&
|
||||||
prev.room.spotlights === next.room.spotlights &&
|
prev.me.roles === next.me.roles &&
|
||||||
prev.room.selectedPeerId === next.room.selectedPeerId
|
prev.peers === next.peers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,9 @@ import Peer from '../Containers/Peer';
|
||||||
import Me from '../Containers/Me';
|
import Me from '../Containers/Me';
|
||||||
|
|
||||||
const RATIO = 1.334;
|
const RATIO = 1.334;
|
||||||
const PADDING_V = 50;
|
const PADDING = 60;
|
||||||
const PADDING_H = 0;
|
|
||||||
|
|
||||||
const styles = () =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
{
|
{
|
||||||
|
|
@ -23,6 +22,7 @@ const styles = () =>
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
flexDirection : 'row',
|
flexDirection : 'row',
|
||||||
flexWrap : 'wrap',
|
flexWrap : 'wrap',
|
||||||
|
overflow : 'hidden',
|
||||||
justifyContent : 'center',
|
justifyContent : 'center',
|
||||||
alignItems : 'center',
|
alignItems : 'center',
|
||||||
alignContent : 'center'
|
alignContent : 'center'
|
||||||
|
|
@ -36,6 +36,14 @@ const styles = () =>
|
||||||
{
|
{
|
||||||
paddingTop : 60,
|
paddingTop : 60,
|
||||||
transition : 'padding .5s'
|
transition : 'padding .5s'
|
||||||
|
},
|
||||||
|
buttonControlBar :
|
||||||
|
{
|
||||||
|
paddingLeft : 60,
|
||||||
|
[theme.breakpoints.down('sm')] :
|
||||||
|
{
|
||||||
|
paddingLeft : 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -66,9 +74,11 @@ class Democratic extends React.PureComponent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = this.peersRef.current.clientWidth - PADDING_H;
|
const width =
|
||||||
const height = this.peersRef.current.clientHeight -
|
this.peersRef.current.clientWidth - (this.props.buttonControlBar ? PADDING : 0);
|
||||||
(this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING_V : PADDING_H);
|
const height =
|
||||||
|
this.peersRef.current.clientHeight -
|
||||||
|
(this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING : 0);
|
||||||
|
|
||||||
let x, y, space;
|
let x, y, space;
|
||||||
|
|
||||||
|
|
@ -130,6 +140,7 @@ class Democratic extends React.PureComponent
|
||||||
spotlightsPeers,
|
spotlightsPeers,
|
||||||
toolbarsVisible,
|
toolbarsVisible,
|
||||||
permanentTopBar,
|
permanentTopBar,
|
||||||
|
buttonControlBar,
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
|
@ -143,7 +154,9 @@ class Democratic extends React.PureComponent
|
||||||
<div
|
<div
|
||||||
className={classnames(
|
className={classnames(
|
||||||
classes.root,
|
classes.root,
|
||||||
toolbarsVisible || permanentTopBar ? classes.showingToolBar : classes.hiddenToolBar
|
toolbarsVisible || permanentTopBar ?
|
||||||
|
classes.showingToolBar : classes.hiddenToolBar,
|
||||||
|
buttonControlBar ? classes.buttonControlBar : null
|
||||||
)}
|
)}
|
||||||
ref={this.peersRef}
|
ref={this.peersRef}
|
||||||
>
|
>
|
||||||
|
|
@ -175,17 +188,21 @@ Democratic.propTypes =
|
||||||
boxes : PropTypes.number,
|
boxes : PropTypes.number,
|
||||||
spotlightsPeers : PropTypes.array.isRequired,
|
spotlightsPeers : PropTypes.array.isRequired,
|
||||||
toolbarsVisible : PropTypes.bool.isRequired,
|
toolbarsVisible : PropTypes.bool.isRequired,
|
||||||
permanentTopBar : PropTypes.bool,
|
permanentTopBar : PropTypes.bool.isRequired,
|
||||||
|
buttonControlBar : PropTypes.bool.isRequired,
|
||||||
|
toolAreaOpen : PropTypes.bool.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
boxes : videoBoxesSelector(state),
|
boxes : videoBoxesSelector(state),
|
||||||
spotlightsPeers : spotlightPeersSelector(state),
|
spotlightsPeers : spotlightPeersSelector(state),
|
||||||
toolbarsVisible : state.room.toolbarsVisible,
|
toolbarsVisible : state.room.toolbarsVisible,
|
||||||
permanentTopBar : state.settings.permanentTopBar
|
permanentTopBar : state.settings.permanentTopBar,
|
||||||
|
buttonControlBar : state.settings.buttonControlBar,
|
||||||
|
toolAreaOpen : state.toolarea.toolAreaOpen
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -202,8 +219,10 @@ export default connect(
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.room.spotlights === next.room.spotlights &&
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
|
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
|
||||||
prev.settings.permanentTopBar === next.settings.permanentTopBar
|
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
|
||||||
|
prev.settings.buttonControlBar === next.settings.buttonControlBar &&
|
||||||
|
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)(withStyles(styles)(Democratic));
|
)(withStyles(styles, { withTheme: true })(Democratic));
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,12 @@ import Peer from '../Containers/Peer';
|
||||||
import SpeakerPeer from '../Containers/SpeakerPeer';
|
import SpeakerPeer from '../Containers/SpeakerPeer';
|
||||||
import Grid from '@material-ui/core/Grid';
|
import Grid from '@material-ui/core/Grid';
|
||||||
|
|
||||||
|
const RATIO = 1.334;
|
||||||
|
const PADDING_V = 40;
|
||||||
|
const PADDING_H = 0;
|
||||||
|
const FILMSTRING_PADDING_V = 10;
|
||||||
|
const FILMSTRING_PADDING_H = 0;
|
||||||
|
|
||||||
const styles = () =>
|
const styles = () =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
|
|
@ -19,25 +25,24 @@ const styles = () =>
|
||||||
height : '100%',
|
height : '100%',
|
||||||
width : '100%',
|
width : '100%',
|
||||||
display : 'grid',
|
display : 'grid',
|
||||||
|
overflow : 'hidden',
|
||||||
gridTemplateColumns : '1fr',
|
gridTemplateColumns : '1fr',
|
||||||
gridTemplateRows : '1.6fr minmax(0, 0.4fr)'
|
gridTemplateRows : '1fr 0.25fr'
|
||||||
},
|
},
|
||||||
speaker :
|
speaker :
|
||||||
{
|
{
|
||||||
gridArea : '1 / 1 / 2 / 2',
|
gridArea : '1 / 1 / 1 / 1',
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
justifyContent : 'center',
|
justifyContent : 'center',
|
||||||
alignItems : 'center',
|
alignItems : 'center'
|
||||||
paddingTop : 40
|
|
||||||
},
|
},
|
||||||
filmStrip :
|
filmStrip :
|
||||||
{
|
{
|
||||||
gridArea : '2 / 1 / 3 / 2'
|
gridArea : '2 / 1 / 2 / 1'
|
||||||
},
|
},
|
||||||
filmItem :
|
filmItem :
|
||||||
{
|
{
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
marginLeft : '6px',
|
|
||||||
border : 'var(--peer-border)',
|
border : 'var(--peer-border)',
|
||||||
'&.selected' :
|
'&.selected' :
|
||||||
{
|
{
|
||||||
|
|
@ -45,8 +50,18 @@ const styles = () =>
|
||||||
},
|
},
|
||||||
'&.active' :
|
'&.active' :
|
||||||
{
|
{
|
||||||
opacity : '0.6'
|
borderColor : 'var(--selected-peer-border-color)'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hiddenToolBar :
|
||||||
|
{
|
||||||
|
paddingTop : 0,
|
||||||
|
transition : 'padding .5s'
|
||||||
|
},
|
||||||
|
showingToolBar :
|
||||||
|
{
|
||||||
|
paddingTop : 60,
|
||||||
|
transition : 'padding .5s'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -58,6 +73,8 @@ class Filmstrip extends React.PureComponent
|
||||||
|
|
||||||
this.resizeTimeout = null;
|
this.resizeTimeout = null;
|
||||||
|
|
||||||
|
this.rootContainer = React.createRef();
|
||||||
|
|
||||||
this.activePeerContainer = React.createRef();
|
this.activePeerContainer = React.createRef();
|
||||||
|
|
||||||
this.filmStripContainer = React.createRef();
|
this.filmStripContainer = React.createRef();
|
||||||
|
|
@ -105,24 +122,38 @@ class Filmstrip extends React.PureComponent
|
||||||
{
|
{
|
||||||
const newState = {};
|
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;
|
const speaker = this.activePeerContainer.current;
|
||||||
|
|
||||||
if (speaker)
|
if (speaker)
|
||||||
{
|
{
|
||||||
let speakerWidth = (speaker.clientWidth - 100);
|
let speakerWidth = (availableWidth - PADDING_H);
|
||||||
|
|
||||||
let speakerHeight = (speakerWidth / 4) * 3;
|
let speakerHeight = speakerWidth / RATIO;
|
||||||
|
|
||||||
if (this.isSharingCamera(this.getActivePeerId()))
|
if (this.isSharingCamera(this.getActivePeerId()))
|
||||||
{
|
{
|
||||||
speakerWidth /= 2;
|
speakerWidth /= 2;
|
||||||
speakerHeight = (speakerWidth / 4) * 3;
|
speakerHeight = speakerWidth / RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (speakerHeight > (speaker.clientHeight - 60))
|
if (speakerHeight > (availableSpeakerHeight - PADDING_V))
|
||||||
{
|
{
|
||||||
speakerHeight = (speaker.clientHeight - 60);
|
speakerHeight = (availableSpeakerHeight - PADDING_V);
|
||||||
speakerWidth = (speakerHeight / 3) * 4;
|
speakerWidth = speakerHeight * RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
newState.speakerWidth = speakerWidth;
|
newState.speakerWidth = speakerWidth;
|
||||||
|
|
@ -133,14 +164,18 @@ class Filmstrip extends React.PureComponent
|
||||||
|
|
||||||
if (filmStrip)
|
if (filmStrip)
|
||||||
{
|
{
|
||||||
let filmStripHeight = filmStrip.clientHeight - 10;
|
let filmStripHeight = availableFilmstripHeight - FILMSTRING_PADDING_V;
|
||||||
|
|
||||||
let filmStripWidth = (filmStripHeight / 3) * 4;
|
let filmStripWidth = filmStripHeight * RATIO;
|
||||||
|
|
||||||
if (filmStripWidth * this.props.boxes > (filmStrip.clientWidth - 50))
|
if (
|
||||||
|
(filmStripWidth * this.props.boxes) >
|
||||||
|
(availableWidth - FILMSTRING_PADDING_H)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
filmStripWidth = (filmStrip.clientWidth - 50) / this.props.boxes;
|
filmStripWidth = (availableWidth - FILMSTRING_PADDING_H) /
|
||||||
filmStripHeight = (filmStripWidth / 4) * 3;
|
this.props.boxes;
|
||||||
|
filmStripHeight = filmStripWidth / RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
newState.filmStripWidth = filmStripWidth;
|
newState.filmStripWidth = filmStripWidth;
|
||||||
|
|
@ -172,27 +207,21 @@ class Filmstrip extends React.PureComponent
|
||||||
window.removeEventListener('resize', this.updateDimensions);
|
window.removeEventListener('resize', this.updateDimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate(nextProps)
|
|
||||||
{
|
|
||||||
if (nextProps !== this.props)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
nextProps.activeSpeakerId != null &&
|
|
||||||
nextProps.activeSpeakerId !== this.props.myId
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// eslint-disable-next-line react/no-did-update-set-state
|
|
||||||
this.setState({
|
|
||||||
lastSpeaker : nextProps.activeSpeakerId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps)
|
componentDidUpdate(prevProps)
|
||||||
{
|
{
|
||||||
if (prevProps !== this.props)
|
if (prevProps !== this.props)
|
||||||
{
|
{
|
||||||
|
if (
|
||||||
|
this.props.activeSpeakerId != null &&
|
||||||
|
this.props.activeSpeakerId !== this.props.myId
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line react/no-did-update-set-state
|
||||||
|
this.setState({
|
||||||
|
lastSpeaker : this.props.activeSpeakerId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.updateDimensions();
|
this.updateDimensions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +234,8 @@ class Filmstrip extends React.PureComponent
|
||||||
myId,
|
myId,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
spotlights,
|
spotlights,
|
||||||
|
toolbarsVisible,
|
||||||
|
permanentTopBar,
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
|
@ -223,7 +254,14 @@ class Filmstrip extends React.PureComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div
|
||||||
|
className={classnames(
|
||||||
|
classes.root,
|
||||||
|
toolbarsVisible || permanentTopBar ?
|
||||||
|
classes.showingToolBar : classes.hiddenToolBar
|
||||||
|
)}
|
||||||
|
ref={this.rootContainer}
|
||||||
|
>
|
||||||
<div className={classes.speaker} ref={this.activePeerContainer}>
|
<div className={classes.speaker} ref={this.activePeerContainer}>
|
||||||
{ peers[activePeerId] &&
|
{ peers[activePeerId] &&
|
||||||
<SpeakerPeer
|
<SpeakerPeer
|
||||||
|
|
@ -245,7 +283,7 @@ class Filmstrip extends React.PureComponent
|
||||||
<Me
|
<Me
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
style={peerStyle}
|
style={peerStyle}
|
||||||
smallButtons
|
smallContainer
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -268,7 +306,7 @@ class Filmstrip extends React.PureComponent
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
id={peerId}
|
id={peerId}
|
||||||
style={peerStyle}
|
style={peerStyle}
|
||||||
smallButtons
|
smallContainer
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -287,28 +325,34 @@ class Filmstrip extends React.PureComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
Filmstrip.propTypes = {
|
Filmstrip.propTypes = {
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
activeSpeakerId : PropTypes.string,
|
activeSpeakerId : PropTypes.string,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
peers : PropTypes.object.isRequired,
|
peers : PropTypes.object.isRequired,
|
||||||
consumers : PropTypes.object.isRequired,
|
consumers : PropTypes.object.isRequired,
|
||||||
myId : PropTypes.string.isRequired,
|
myId : PropTypes.string.isRequired,
|
||||||
selectedPeerId : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlights : PropTypes.array.isRequired,
|
spotlights : PropTypes.array.isRequired,
|
||||||
boxes : PropTypes.number,
|
boxes : PropTypes.number,
|
||||||
classes : PropTypes.object.isRequired
|
toolbarsVisible : PropTypes.bool.isRequired,
|
||||||
|
toolAreaOpen : PropTypes.bool.isRequired,
|
||||||
|
permanentTopBar : PropTypes.bool,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
activeSpeakerId : state.room.activeSpeakerId,
|
activeSpeakerId : state.room.activeSpeakerId,
|
||||||
selectedPeerId : state.room.selectedPeerId,
|
selectedPeerId : state.room.selectedPeerId,
|
||||||
peers : state.peers,
|
peers : state.peers,
|
||||||
consumers : state.consumers,
|
consumers : state.consumers,
|
||||||
myId : state.me.id,
|
myId : state.me.id,
|
||||||
spotlights : state.room.spotlights,
|
spotlights : state.room.spotlights,
|
||||||
boxes : videoBoxesSelector(state)
|
boxes : videoBoxesSelector(state),
|
||||||
|
toolbarsVisible : state.room.toolbarsVisible,
|
||||||
|
toolAreaOpen : state.toolarea.toolAreaOpen,
|
||||||
|
permanentTopBar : state.settings.permanentTopBar
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -322,6 +366,9 @@ export default withRoomContext(connect(
|
||||||
return (
|
return (
|
||||||
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||||
prev.room.selectedPeerId === next.room.selectedPeerId &&
|
prev.room.selectedPeerId === next.room.selectedPeerId &&
|
||||||
|
prev.room.toolbarsVisible === next.room.toolbarsVisible &&
|
||||||
|
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen &&
|
||||||
|
prev.settings.permanentTopBar === next.settings.permanentTopBar &&
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.room.spotlights === next.room.spotlights &&
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
|
||||||
import { withSnackbar } from 'notistack';
|
import { withSnackbar } from 'notistack';
|
||||||
import * as notificationActions from '../../actions/notificationActions';
|
import * as notificationActions from '../../actions/notificationActions';
|
||||||
|
|
||||||
|
const notificationPosition = window.config.notificationPosition || 'right';
|
||||||
|
|
||||||
class Notifications extends Component
|
class Notifications extends Component
|
||||||
{
|
{
|
||||||
displayed = [];
|
displayed = [];
|
||||||
|
|
@ -45,7 +47,7 @@ class Notifications extends Component
|
||||||
autoHideDuration : notification.timeout,
|
autoHideDuration : notification.timeout,
|
||||||
anchorOrigin : {
|
anchorOrigin : {
|
||||||
vertical : 'bottom',
|
vertical : 'bottom',
|
||||||
horizontal : 'left'
|
horizontal : notificationPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { micConsumerSelector } from '../Selectors';
|
import { passiveMicConsumerSelector } from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import PeerAudio from './PeerAudio';
|
import PeerAudio from './PeerAudio';
|
||||||
|
|
||||||
const AudioPeers = (props) =>
|
const AudioPeers = (props) =>
|
||||||
{
|
{
|
||||||
const {
|
const {
|
||||||
micConsumers
|
micConsumers,
|
||||||
|
audioOutputDevice
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -19,6 +20,7 @@ const AudioPeers = (props) =>
|
||||||
<PeerAudio
|
<PeerAudio
|
||||||
key={micConsumer.id}
|
key={micConsumer.id}
|
||||||
audioTrack={micConsumer.track}
|
audioTrack={micConsumer.track}
|
||||||
|
audioOutputDevice={audioOutputDevice}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -29,12 +31,14 @@ const AudioPeers = (props) =>
|
||||||
|
|
||||||
AudioPeers.propTypes =
|
AudioPeers.propTypes =
|
||||||
{
|
{
|
||||||
micConsumers : PropTypes.array
|
micConsumers : PropTypes.array,
|
||||||
|
audioOutputDevice : PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
({
|
({
|
||||||
micConsumers : micConsumerSelector(state)
|
micConsumers : passiveMicConsumerSelector(state),
|
||||||
|
audioOutputDevice : state.settings.selectedAudioOutputDevice
|
||||||
});
|
});
|
||||||
|
|
||||||
const AudioPeersContainer = connect(
|
const AudioPeersContainer = connect(
|
||||||
|
|
@ -45,7 +49,10 @@ const AudioPeersContainer = connect(
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.consumers === next.consumers
|
prev.consumers === next.consumers &&
|
||||||
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
|
prev.settings.selectedAudioOutputDevice ===
|
||||||
|
next.settings.selectedAudioOutputDevice
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export default class PeerAudio extends React.PureComponent
|
||||||
// Latest received audio track.
|
// Latest received audio track.
|
||||||
// @type {MediaStreamTrack}
|
// @type {MediaStreamTrack}
|
||||||
this._audioTrack = null;
|
this._audioTrack = null;
|
||||||
|
this._audioOutputDevice = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render()
|
render()
|
||||||
|
|
@ -24,17 +25,21 @@ export default class PeerAudio extends React.PureComponent
|
||||||
|
|
||||||
componentDidMount()
|
componentDidMount()
|
||||||
{
|
{
|
||||||
const { audioTrack } = this.props;
|
const { audioTrack, audioOutputDevice } = this.props;
|
||||||
|
|
||||||
this._setTrack(audioTrack);
|
this._setTrack(audioTrack);
|
||||||
|
this._setOutputDevice(audioOutputDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
componentDidUpdate(prevProps)
|
||||||
UNSAFE_componentWillReceiveProps(nextProps)
|
|
||||||
{
|
{
|
||||||
const { audioTrack } = nextProps;
|
if (prevProps !== this.props)
|
||||||
|
{
|
||||||
|
const { audioTrack, audioOutputDevice } = this.props;
|
||||||
|
|
||||||
this._setTrack(audioTrack);
|
this._setTrack(audioTrack);
|
||||||
|
this._setOutputDevice(audioOutputDevice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setTrack(audioTrack)
|
_setTrack(audioTrack)
|
||||||
|
|
@ -60,9 +65,23 @@ export default class PeerAudio extends React.PureComponent
|
||||||
audio.srcObject = null;
|
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 =
|
PeerAudio.propTypes =
|
||||||
{
|
{
|
||||||
audioTrack : PropTypes.any
|
audioTrack : PropTypes.any,
|
||||||
|
audioOutputDevice : PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import CookieConsent from 'react-cookie-consent';
|
import CookieConsent from 'react-cookie-consent';
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||||
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
|
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
|
||||||
|
import Drawer from '@material-ui/core/Drawer';
|
||||||
import Hidden from '@material-ui/core/Hidden';
|
import Hidden from '@material-ui/core/Hidden';
|
||||||
import Notifications from './Notifications/Notifications';
|
import Notifications from './Notifications/Notifications';
|
||||||
import MeetingDrawer from './MeetingDrawer/MeetingDrawer';
|
import MeetingDrawer from './MeetingDrawer/MeetingDrawer';
|
||||||
|
|
@ -23,8 +24,13 @@ import VideoWindow from './VideoWindow/VideoWindow';
|
||||||
import LockDialog from './AccessControl/LockDialog/LockDialog';
|
import LockDialog from './AccessControl/LockDialog/LockDialog';
|
||||||
import Settings from './Settings/Settings';
|
import Settings from './Settings/Settings';
|
||||||
import TopBar from './Controls/TopBar';
|
import TopBar from './Controls/TopBar';
|
||||||
|
import WakeLock from 'react-wakelock-react16';
|
||||||
|
import ExtraVideo from './Controls/ExtraVideo';
|
||||||
|
import ButtonControlBar from './Controls/ButtonControlBar';
|
||||||
|
import Help from './Controls/Help';
|
||||||
|
import About from './Controls/About';
|
||||||
|
|
||||||
const TIMEOUT = 5 * 1000;
|
const TIMEOUT = window.config.hideTimeout || 5000;
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -40,6 +46,27 @@ const styles = (theme) =>
|
||||||
backgroundSize : 'cover',
|
backgroundSize : 'cover',
|
||||||
backgroundRepeat : 'no-repeat'
|
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 :
|
drawerPaper :
|
||||||
{
|
{
|
||||||
width : '30vw',
|
width : '30vw',
|
||||||
|
|
@ -138,7 +165,11 @@ class Room extends React.PureComponent
|
||||||
{
|
{
|
||||||
const {
|
const {
|
||||||
room,
|
room,
|
||||||
|
browser,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
|
showNotifications,
|
||||||
|
buttonControlBar,
|
||||||
|
drawerOverlayed,
|
||||||
toolAreaOpen,
|
toolAreaOpen,
|
||||||
toggleToolArea,
|
toggleToolArea,
|
||||||
classes,
|
classes,
|
||||||
|
|
@ -151,19 +182,21 @@ class Room extends React.PureComponent
|
||||||
democratic : Democratic
|
democratic : Democratic
|
||||||
}[room.mode];
|
}[room.mode];
|
||||||
|
|
||||||
|
const container = window !== undefined ? window.document.body : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
{ !isElectron() &&
|
{ !isElectron() &&
|
||||||
<CookieConsent
|
<CookieConsent
|
||||||
buttonText={
|
buttonText={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id = 'room.consentUnderstand'
|
id='room.consentUnderstand'
|
||||||
defaultMessage = 'I understand'
|
defaultMessage='I understand'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id = 'room.cookieConsent'
|
id='room.cookieConsent'
|
||||||
defaultMessage='This website uses cookies to enhance the user experience'
|
defaultMessage='This website uses cookies to enhance the user experience'
|
||||||
/>
|
/>
|
||||||
</CookieConsent>
|
</CookieConsent>
|
||||||
|
|
@ -175,7 +208,9 @@ class Room extends React.PureComponent
|
||||||
|
|
||||||
<AudioPeers />
|
<AudioPeers />
|
||||||
|
|
||||||
<Notifications />
|
{ showNotifications &&
|
||||||
|
<Notifications />
|
||||||
|
}
|
||||||
|
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
|
||||||
|
|
@ -185,25 +220,55 @@ class Room extends React.PureComponent
|
||||||
onFullscreen={this.handleToggleFullscreen}
|
onFullscreen={this.handleToggleFullscreen}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<nav>
|
{ (browser.platform === 'mobile' || drawerOverlayed) ?
|
||||||
<Hidden implementation='css'>
|
<nav>
|
||||||
<SwipeableDrawer
|
<Hidden implementation='css'>
|
||||||
variant='temporary'
|
<SwipeableDrawer
|
||||||
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
|
container={container}
|
||||||
open={toolAreaOpen}
|
variant='temporary'
|
||||||
onClose={() => toggleToolArea()}
|
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
|
||||||
onOpen={() => toggleToolArea()}
|
open={toolAreaOpen}
|
||||||
classes={{
|
onClose={() => toggleToolArea()}
|
||||||
paper : classes.drawerPaper
|
onOpen={() => toggleToolArea()}
|
||||||
}}
|
classes={{
|
||||||
>
|
paper : classes.drawerPaper
|
||||||
<MeetingDrawer closeDrawer={toggleToolArea} />
|
}}
|
||||||
</SwipeableDrawer>
|
ModalProps={{
|
||||||
</Hidden>
|
keepMounted : true // Better open performance on mobile.
|
||||||
</nav>
|
}}
|
||||||
|
>
|
||||||
|
<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} />
|
<View advancedMode={advancedMode} />
|
||||||
|
|
||||||
|
{ buttonControlBar &&
|
||||||
|
<ButtonControlBar />
|
||||||
|
}
|
||||||
|
|
||||||
{ room.lockDialogOpen &&
|
{ room.lockDialogOpen &&
|
||||||
<LockDialog />
|
<LockDialog />
|
||||||
}
|
}
|
||||||
|
|
@ -211,6 +276,17 @@ class Room extends React.PureComponent
|
||||||
{ room.settingsOpen &&
|
{ room.settingsOpen &&
|
||||||
<Settings />
|
<Settings />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ room.extraVideoOpen &&
|
||||||
|
<ExtraVideo />
|
||||||
|
}
|
||||||
|
{ room.helpOpen &&
|
||||||
|
<Help />
|
||||||
|
}
|
||||||
|
{ room.aboutOpen &&
|
||||||
|
<About />
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +295,11 @@ class Room extends React.PureComponent
|
||||||
Room.propTypes =
|
Room.propTypes =
|
||||||
{
|
{
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
|
browser : PropTypes.object.isRequired,
|
||||||
advancedMode : PropTypes.bool.isRequired,
|
advancedMode : PropTypes.bool.isRequired,
|
||||||
|
showNotifications : PropTypes.bool.isRequired,
|
||||||
|
buttonControlBar : PropTypes.bool.isRequired,
|
||||||
|
drawerOverlayed : PropTypes.bool.isRequired,
|
||||||
toolAreaOpen : PropTypes.bool.isRequired,
|
toolAreaOpen : PropTypes.bool.isRequired,
|
||||||
setToolbarsVisible : PropTypes.func.isRequired,
|
setToolbarsVisible : PropTypes.func.isRequired,
|
||||||
toggleToolArea : PropTypes.func.isRequired,
|
toggleToolArea : PropTypes.func.isRequired,
|
||||||
|
|
@ -229,9 +309,13 @@ Room.propTypes =
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
({
|
({
|
||||||
room : state.room,
|
room : state.room,
|
||||||
advancedMode : state.settings.advancedMode,
|
browser : state.me.browser,
|
||||||
toolAreaOpen : state.toolarea.toolAreaOpen
|
advancedMode : state.settings.advancedMode,
|
||||||
|
showNotifications : state.settings.showNotifications,
|
||||||
|
buttonControlBar : state.settings.buttonControlBar,
|
||||||
|
drawerOverlayed : state.settings.drawerOverlayed,
|
||||||
|
toolAreaOpen : state.toolarea.toolAreaOpen
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
|
|
@ -255,7 +339,11 @@ export default connect(
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room === next.room &&
|
prev.room === next.room &&
|
||||||
|
prev.me.browser === next.me.browser &&
|
||||||
prev.settings.advancedMode === next.settings.advancedMode &&
|
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
|
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { createSelector } from 'reselect';
|
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 producersSelect = (state) => state.producers;
|
||||||
const consumersSelect = (state) => state.consumers;
|
const consumersSelect = (state) => state.consumers;
|
||||||
const spotlightsSelector = (state) => state.room.spotlights;
|
const spotlightsSelector = (state) => state.room.spotlights;
|
||||||
|
|
@ -13,6 +16,11 @@ const peersKeySelector = createSelector(
|
||||||
(peers) => Object.keys(peers)
|
(peers) => Object.keys(peers)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const peersValueSelector = createSelector(
|
||||||
|
peersSelector,
|
||||||
|
(peers) => Object.values(peers)
|
||||||
|
);
|
||||||
|
|
||||||
export const lobbyPeersKeySelector = createSelector(
|
export const lobbyPeersKeySelector = createSelector(
|
||||||
lobbyPeersSelector,
|
lobbyPeersSelector,
|
||||||
(lobbyPeers) => Object.keys(lobbyPeers)
|
(lobbyPeers) => Object.keys(lobbyPeers)
|
||||||
|
|
@ -33,6 +41,11 @@ export const screenProducersSelector = createSelector(
|
||||||
(producers) => Object.values(producers).filter((producer) => producer.source === 'screen')
|
(producers) => Object.values(producers).filter((producer) => producer.source === 'screen')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const extraVideoProducersSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).filter((producer) => producer.source === 'extravideo')
|
||||||
|
);
|
||||||
|
|
||||||
export const micProducerSelector = createSelector(
|
export const micProducerSelector = createSelector(
|
||||||
producersSelect,
|
producersSelect,
|
||||||
(producers) => Object.values(producers).find((producer) => producer.source === 'mic')
|
(producers) => Object.values(producers).find((producer) => producer.source === 'mic')
|
||||||
|
|
@ -63,6 +76,33 @@ export const screenConsumerSelector = createSelector(
|
||||||
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen')
|
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const spotlightScreenConsumerSelector = createSelector(
|
||||||
|
spotlightsSelector,
|
||||||
|
consumersSelect,
|
||||||
|
(spotlights, consumers) =>
|
||||||
|
Object.values(consumers).filter(
|
||||||
|
(consumer) => consumer.source === 'screen' && spotlights.includes(consumer.peerId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const spotlightExtraVideoConsumerSelector = createSelector(
|
||||||
|
spotlightsSelector,
|
||||||
|
consumersSelect,
|
||||||
|
(spotlights, consumers) =>
|
||||||
|
Object.values(consumers).filter(
|
||||||
|
(consumer) => consumer.source === 'extravideo' && spotlights.includes(consumer.peerId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const passiveMicConsumerSelector = createSelector(
|
||||||
|
spotlightsSelector,
|
||||||
|
consumersSelect,
|
||||||
|
(spotlights, consumers) =>
|
||||||
|
Object.values(consumers).filter(
|
||||||
|
(consumer) => consumer.source === 'mic' && !spotlights.includes(consumer.peerId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export const spotlightsLengthSelector = createSelector(
|
export const spotlightsLengthSelector = createSelector(
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
(spotlights) => spotlights.length
|
(spotlights) => spotlights.length
|
||||||
|
|
@ -74,35 +114,83 @@ export const spotlightPeersSelector = createSelector(
|
||||||
(spotlights, peers) => peers.filter((peerId) => spotlights.includes(peerId))
|
(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(
|
export const peersLengthSelector = createSelector(
|
||||||
peersSelector,
|
peersSelector,
|
||||||
(peers) => Object.values(peers).length
|
(peers) => Object.values(peers).length
|
||||||
);
|
);
|
||||||
|
|
||||||
export const passivePeersSelector = createSelector(
|
export const passivePeersSelector = createSelector(
|
||||||
peersKeySelector,
|
peersValueSelector,
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
(peers, spotlights) => peers.filter((peerId) => !spotlights.includes(peerId))
|
(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)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const videoBoxesSelector = createSelector(
|
export const videoBoxesSelector = createSelector(
|
||||||
spotlightsLengthSelector,
|
spotlightsLengthSelector,
|
||||||
screenProducersSelector,
|
screenProducersSelector,
|
||||||
screenConsumerSelector,
|
spotlightScreenConsumerSelector,
|
||||||
(spotlightsLength, screenProducers, screenConsumers) =>
|
extraVideoProducersSelector,
|
||||||
spotlightsLength + 1 + screenProducers.length + screenConsumers.length
|
spotlightExtraVideoConsumerSelector,
|
||||||
|
(
|
||||||
|
spotlightsLength,
|
||||||
|
screenProducers,
|
||||||
|
screenConsumers,
|
||||||
|
extraVideoProducers,
|
||||||
|
extraVideoConsumers
|
||||||
|
) =>
|
||||||
|
spotlightsLength + 1 + screenProducers.length +
|
||||||
|
screenConsumers.length + extraVideoProducers.length +
|
||||||
|
extraVideoConsumers.length
|
||||||
);
|
);
|
||||||
|
|
||||||
export const meProducersSelector = createSelector(
|
export const meProducersSelector = createSelector(
|
||||||
micProducerSelector,
|
micProducerSelector,
|
||||||
webcamProducerSelector,
|
webcamProducerSelector,
|
||||||
screenProducerSelector,
|
screenProducerSelector,
|
||||||
(micProducer, webcamProducer, screenProducer) =>
|
extraVideoProducersSelector,
|
||||||
|
(micProducer, webcamProducer, screenProducer, extraVideoProducers) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
micProducer,
|
micProducer,
|
||||||
webcamProducer,
|
webcamProducer,
|
||||||
screenProducer
|
screenProducer,
|
||||||
|
extraVideoProducers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -125,8 +213,60 @@ export const makePeerConsumerSelector = () =>
|
||||||
consumersArray.find((consumer) => consumer.source === 'webcam');
|
consumersArray.find((consumer) => consumer.source === 'webcam');
|
||||||
const screenConsumer =
|
const screenConsumer =
|
||||||
consumersArray.find((consumer) => consumer.source === 'screen');
|
consumersArray.find((consumer) => consumer.source === 'screen');
|
||||||
|
const extraVideoConsumers =
|
||||||
|
consumersArray.filter((consumer) => consumer.source === 'extravideo');
|
||||||
|
|
||||||
return { micConsumer, webcamConsumer, screenConsumer };
|
return { micConsumer, webcamConsumer, screenConsumer, extraVideoConsumers };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
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)));
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
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));
|
||||||
|
|
@ -0,0 +1,578 @@
|
||||||
|
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)));
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
|
||||||
import * as roomActions from '../../actions/roomActions';
|
import * as roomActions from '../../actions/roomActions';
|
||||||
import * as settingsActions from '../../actions/settingsActions';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import Tabs from '@material-ui/core/Tabs';
|
||||||
|
import Tab from '@material-ui/core/Tab';
|
||||||
|
import MediaSettings from './MediaSettings';
|
||||||
|
import AppearenceSettings from './AppearenceSettings';
|
||||||
|
import AdvancedSettings from './AdvancedSettings';
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
|
||||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
const tabs =
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
[
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
'media',
|
||||||
import Select from '@material-ui/core/Select';
|
'appearence',
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
'advanced'
|
||||||
|
];
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -43,99 +46,27 @@ const styles = (theme) =>
|
||||||
width : '90vw'
|
width : '90vw'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setting :
|
tabsHeader :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(2)
|
flexGrow : 1
|
||||||
},
|
|
||||||
formControl :
|
|
||||||
{
|
|
||||||
display : 'flex'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Settings = ({
|
const Settings = ({
|
||||||
roomClient,
|
currentSettingsTab,
|
||||||
room,
|
settingsOpen,
|
||||||
me,
|
|
||||||
settings,
|
|
||||||
onToggleAdvancedMode,
|
|
||||||
onTogglePermanentTopBar,
|
|
||||||
handleCloseSettings,
|
handleCloseSettings,
|
||||||
handleChangeMode,
|
setSettingsTab,
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const modes = [ {
|
|
||||||
value : 'democratic',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.democratic',
|
|
||||||
defaultMessage : 'Democratic view'
|
|
||||||
})
|
|
||||||
}, {
|
|
||||||
value : 'filmstrip',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.filmstrip',
|
|
||||||
defaultMessage : 'Filmstrip view'
|
|
||||||
})
|
|
||||||
} ];
|
|
||||||
|
|
||||||
const resolutions = [ {
|
|
||||||
value : 'low',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.low',
|
|
||||||
defaultMessage : 'Low'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value : 'medium',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.medium',
|
|
||||||
defaultMessage : 'Medium'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value : 'high',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.high',
|
|
||||||
defaultMessage : 'High (HD)'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value : 'veryhigh',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.veryHigh',
|
|
||||||
defaultMessage : 'Very high (FHD)'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value : 'ultra',
|
|
||||||
label : intl.formatMessage({
|
|
||||||
id : 'label.ultra',
|
|
||||||
defaultMessage : 'Ultra (UHD)'
|
|
||||||
})
|
|
||||||
} ];
|
|
||||||
|
|
||||||
let webcams;
|
|
||||||
|
|
||||||
if (me.webcamDevices)
|
|
||||||
webcams = Object.values(me.webcamDevices);
|
|
||||||
else
|
|
||||||
webcams = [];
|
|
||||||
|
|
||||||
let audioDevices;
|
|
||||||
|
|
||||||
if (me.audioDevices)
|
|
||||||
audioDevices = Object.values(me.audioDevices);
|
|
||||||
else
|
|
||||||
audioDevices = [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
className={classes.root}
|
className={classes.root}
|
||||||
open={room.settingsOpen}
|
open={settingsOpen}
|
||||||
onClose={() => handleCloseSettings({ settingsOpen: false })}
|
onClose={() => handleCloseSettings(false)}
|
||||||
classes={{
|
classes={{
|
||||||
paper : classes.dialogPaper
|
paper : classes.dialogPaper
|
||||||
}}
|
}}
|
||||||
|
|
@ -146,201 +77,40 @@ const Settings = ({
|
||||||
defaultMessage='Settings'
|
defaultMessage='Settings'
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<form className={classes.setting} autoComplete='off'>
|
<Tabs
|
||||||
<FormControl className={classes.formControl}>
|
className={classes.tabsHeader}
|
||||||
<Select
|
value={tabs.indexOf(currentSettingsTab)}
|
||||||
value={settings.selectedWebcam || ''}
|
onChange={(event, value) => setSettingsTab(tabs[value])}
|
||||||
onChange={(event) =>
|
indicatorColor='primary'
|
||||||
{
|
textColor='primary'
|
||||||
if (event.target.value)
|
variant='fullWidth'
|
||||||
roomClient.changeWebcam(event.target.value);
|
>
|
||||||
}}
|
<Tab
|
||||||
displayEmpty
|
label={
|
||||||
name={intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'settings.camera',
|
id : 'label.media',
|
||||||
defaultMessage : 'Camera'
|
defaultMessage : 'Media'
|
||||||
})}
|
})
|
||||||
autoWidth
|
}
|
||||||
className={classes.selectEmpty}
|
/>
|
||||||
disabled={webcams.length === 0 || me.webcamInProgress}
|
<Tab
|
||||||
>
|
label={intl.formatMessage({
|
||||||
{ webcams.map((webcam, index) =>
|
id : 'label.appearance',
|
||||||
{
|
defaultMessage : 'Appearence'
|
||||||
return (
|
})}
|
||||||
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
|
/>
|
||||||
);
|
<Tab
|
||||||
})}
|
label={intl.formatMessage({
|
||||||
</Select>
|
id : 'label.advanced',
|
||||||
<FormHelperText>
|
defaultMessage : 'Advanced'
|
||||||
{ webcams.length > 0 ?
|
})}
|
||||||
intl.formatMessage({
|
/>
|
||||||
id : 'settings.selectCamera',
|
</Tabs>
|
||||||
defaultMessage : 'Select video device'
|
{currentSettingsTab === 'media' && <MediaSettings />}
|
||||||
})
|
{currentSettingsTab === 'appearence' && <AppearenceSettings />}
|
||||||
:
|
{currentSettingsTab === 'advanced' && <AdvancedSettings />}
|
||||||
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'
|
|
||||||
/>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
<form className={classes.setting} autoComplete='off'>
|
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={room.mode || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
handleChangeMode(event.target.value);
|
|
||||||
}}
|
|
||||||
name={intl.formatMessage({
|
|
||||||
id : 'settings.layout',
|
|
||||||
defaultMessage : 'Room layout'
|
|
||||||
})}
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
>
|
|
||||||
{ modes.map((mode, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={index} value={mode.value}>
|
|
||||||
{mode.label}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.selectRoomLayout'
|
|
||||||
defaultMessage='Select room layout'
|
|
||||||
/>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</form>
|
|
||||||
<FormControlLabel
|
|
||||||
className={classes.setting}
|
|
||||||
control={<Checkbox checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id : 'settings.advancedMode',
|
|
||||||
defaultMessage : 'Advanced mode'
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
{ settings.advancedMode &&
|
|
||||||
<React.Fragment>
|
|
||||||
<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 : 'settings.permanentTopBar',
|
|
||||||
defaultMessage : 'Permanent top bar'
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => handleCloseSettings({ settingsOpen: false })} color='primary'>
|
<Button onClick={() => handleCloseSettings(false)} color='primary'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='label.close'
|
id='label.close'
|
||||||
defaultMessage='Close'
|
defaultMessage='Close'
|
||||||
|
|
@ -353,34 +123,25 @@ const Settings = ({
|
||||||
|
|
||||||
Settings.propTypes =
|
Settings.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
currentSettingsTab : PropTypes.string.isRequired,
|
||||||
me : appPropTypes.Me.isRequired,
|
settingsOpen : PropTypes.bool.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
handleCloseSettings : PropTypes.func.isRequired,
|
||||||
settings : PropTypes.object.isRequired,
|
setSettingsTab : PropTypes.func.isRequired,
|
||||||
onToggleAdvancedMode : PropTypes.func.isRequired,
|
classes : PropTypes.object.isRequired
|
||||||
onTogglePermanentTopBar : PropTypes.func.isRequired,
|
|
||||||
handleChangeMode : PropTypes.func.isRequired,
|
|
||||||
handleCloseSettings : PropTypes.func.isRequired,
|
|
||||||
classes : PropTypes.object.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
({
|
||||||
return {
|
currentSettingsTab : state.room.currentSettingsTab,
|
||||||
me : state.me,
|
settingsOpen : state.room.settingsOpen
|
||||||
room : state.room,
|
});
|
||||||
settings : state.settings
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
onToggleAdvancedMode : settingsActions.toggleAdvancedMode,
|
handleCloseSettings : roomActions.setSettingsOpen,
|
||||||
onTogglePermanentTopBar : settingsActions.togglePermanentTopBar,
|
setSettingsTab : roomActions.setSettingsTab
|
||||||
handleChangeMode : roomActions.setDisplayMode,
|
|
||||||
handleCloseSettings : roomActions.setSettingsOpen
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
null,
|
null,
|
||||||
|
|
@ -388,10 +149,9 @@ export default withRoomContext(connect(
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.me === next.me &&
|
prev.room.currentSettingsTab === next.room.currentSettingsTab &&
|
||||||
prev.room === next.room &&
|
prev.room.settingsOpen === next.room.settingsOpen
|
||||||
prev.settings === next.settings
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)(withStyles(styles)(Settings)));
|
)(withStyles(styles)(Settings));
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
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);
|
||||||
|
|
@ -96,11 +96,6 @@ const FullScreenView = (props) =>
|
||||||
!consumer.remotelyPaused
|
!consumer.remotelyPaused
|
||||||
);
|
);
|
||||||
|
|
||||||
let consumerProfile;
|
|
||||||
|
|
||||||
if (consumer)
|
|
||||||
consumerProfile = consumer.profile;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.controls}>
|
<div className={classes.controls}>
|
||||||
|
|
@ -121,9 +116,25 @@ const FullScreenView = (props) =>
|
||||||
<VideoView
|
<VideoView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoContain
|
videoContain
|
||||||
videoTrack={consumer ? consumer.track : null}
|
consumerSpatialLayers={consumer ? consumer.spatialLayers : null}
|
||||||
|
consumerTemporalLayers={consumer ? consumer.temporalLayers : null}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
consumer ? consumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
consumer ? consumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
consumer ? consumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
consumer ? consumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={consumer && consumer.type !== 'simple'}
|
||||||
|
videoTrack={consumer && consumer.track}
|
||||||
videoVisible={consumerVisible}
|
videoVisible={consumerVisible}
|
||||||
videoProfile={consumerProfile}
|
videoCodec={consumer && consumer.codec}
|
||||||
|
videoScore={consumer ? consumer.score : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -135,7 +146,7 @@ FullScreenView.propTypes =
|
||||||
consumer : appPropTypes.Consumer,
|
consumer : appPropTypes.Consumer,
|
||||||
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
||||||
toolbarsVisible : PropTypes.bool,
|
toolbarsVisible : PropTypes.bool,
|
||||||
permanentTopBar : PropTypes.bool,
|
permanentTopBar : PropTypes.bool,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -143,7 +154,7 @@ const mapStateToProps = (state) =>
|
||||||
({
|
({
|
||||||
consumer : state.consumers[state.room.fullScreenConsumer],
|
consumer : state.consumers[state.room.fullScreenConsumer],
|
||||||
toolbarsVisible : state.room.toolbarsVisible,
|
toolbarsVisible : state.room.toolbarsVisible,
|
||||||
permanentTopBar : state.settings.permanentTopBar
|
permanentTopBar : state.settings.permanentTopBar
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,14 @@ class FullView extends React.PureComponent
|
||||||
this._setTracks(videoTrack);
|
this._setTracks(videoTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate()
|
componentDidUpdate(prevProps)
|
||||||
{
|
{
|
||||||
const { videoTrack } = this.props;
|
if (prevProps !== this.props)
|
||||||
|
{
|
||||||
|
const { videoTrack } = this.props;
|
||||||
|
|
||||||
this._setTracks(videoTrack);
|
this._setTracks(videoTrack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setTracks(videoTrack)
|
_setTracks(videoTrack)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,15 @@ import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import EditableInput from '../Controls/EditableInput';
|
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) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -16,6 +25,7 @@ const styles = (theme) =>
|
||||||
flexDirection : 'column',
|
flexDirection : 'column',
|
||||||
overflow : 'hidden'
|
overflow : 'hidden'
|
||||||
},
|
},
|
||||||
|
|
||||||
video :
|
video :
|
||||||
{
|
{
|
||||||
flex : '100 100 auto',
|
flex : '100 100 auto',
|
||||||
|
|
@ -60,24 +70,66 @@ const styles = (theme) =>
|
||||||
{
|
{
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
transitionProperty : 'opacity',
|
transitionProperty : 'opacity',
|
||||||
transitionDuration : '.15s',
|
transitionDuration : '.15s'
|
||||||
'&.hidden' :
|
|
||||||
{
|
|
||||||
opacity : 0,
|
|
||||||
transitionDuration : '0s'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
box :
|
box :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(0.5),
|
padding : theme.spacing(0.5),
|
||||||
borderRadius : 2,
|
borderRadius : 2,
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.25)',
|
userSelect : 'none',
|
||||||
'& p' :
|
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' :
|
||||||
{
|
{
|
||||||
userSelect : 'none',
|
marginLeft : 'auto',
|
||||||
margin : 0,
|
width : 30
|
||||||
color : 'rgba(255, 255, 255, 0.7)',
|
},
|
||||||
fontSize : '0.8em'
|
'&.hidden' :
|
||||||
|
{
|
||||||
|
opacity : 0,
|
||||||
|
transitionDuration : '0s'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
peer :
|
peer :
|
||||||
|
|
@ -119,6 +171,10 @@ class VideoView extends React.PureComponent
|
||||||
videoHeight : null
|
videoHeight : null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Latest received audio track
|
||||||
|
// @type {MediaStreamTrack}
|
||||||
|
this._audioTrack = null;
|
||||||
|
|
||||||
// Latest received video track.
|
// Latest received video track.
|
||||||
// @type {MediaStreamTrack}
|
// @type {MediaStreamTrack}
|
||||||
this._videoTrack = null;
|
this._videoTrack = null;
|
||||||
|
|
@ -132,16 +188,16 @@ class VideoView extends React.PureComponent
|
||||||
const {
|
const {
|
||||||
isMe,
|
isMe,
|
||||||
isScreen,
|
isScreen,
|
||||||
|
isExtraVideo,
|
||||||
|
showQuality,
|
||||||
displayName,
|
displayName,
|
||||||
showPeerInfo,
|
showPeerInfo,
|
||||||
videoContain,
|
videoContain,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
videoVisible,
|
videoVisible,
|
||||||
videoMultiLayer,
|
videoMultiLayer,
|
||||||
// audioScore,
|
audioScore,
|
||||||
// videoScore,
|
videoScore,
|
||||||
// consumerSpatialLayers,
|
|
||||||
// consumerTemporalLayers,
|
|
||||||
consumerCurrentSpatialLayer,
|
consumerCurrentSpatialLayer,
|
||||||
consumerCurrentTemporalLayer,
|
consumerCurrentTemporalLayer,
|
||||||
consumerPreferredSpatialLayer,
|
consumerPreferredSpatialLayer,
|
||||||
|
|
@ -150,7 +206,8 @@ class VideoView extends React.PureComponent
|
||||||
videoCodec,
|
videoCodec,
|
||||||
onChangeDisplayName,
|
onChangeDisplayName,
|
||||||
children,
|
children,
|
||||||
classes
|
classes,
|
||||||
|
netInfo
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -158,55 +215,172 @@ class VideoView extends React.PureComponent
|
||||||
videoHeight
|
videoHeight
|
||||||
} = this.state;
|
} = 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 (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.info}>
|
<div className={classes.info}>
|
||||||
<div className={classnames(classes.media,
|
<div className={classes.media}>
|
||||||
{
|
<div className={classnames(classes.box, 'left', { hidden: !advancedMode })}>
|
||||||
hidden : !advancedMode
|
{ audioCodec &&
|
||||||
})}
|
<React.Fragment>
|
||||||
>
|
<span className={'AcodL'}>Acod: </span>
|
||||||
<div className={classes.box}>
|
<span className={'Acod'}>
|
||||||
{ audioCodec && <p>{audioCodec}</p> }
|
{audioCodec}
|
||||||
|
</span>
|
||||||
{ videoCodec &&
|
</React.Fragment>
|
||||||
<p>
|
|
||||||
{videoCodec}
|
|
||||||
</p>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ videoMultiLayer &&
|
{ videoCodec &&
|
||||||
<p>
|
<React.Fragment>
|
||||||
{`current spatial-temporal layers: ${consumerCurrentSpatialLayer} ${consumerCurrentTemporalLayer}`}
|
<span className={'VcodL'}>Vcod: </span>
|
||||||
<br />
|
<span className={'Vcod'}>
|
||||||
{`preferred spatial-temporal layers: ${consumerPreferredSpatialLayer} ${consumerPreferredTemporalLayer}`}
|
{videoCodec}
|
||||||
</p>
|
</span>
|
||||||
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
||||||
{ (videoVisible && videoWidth !== null) &&
|
{ (videoVisible && videoWidth !== null) &&
|
||||||
<p>{videoWidth}x{videoHeight}</p>
|
<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>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ 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>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{ showQuality &&
|
||||||
|
<div className={classnames(classes.box, 'right')}>
|
||||||
|
{
|
||||||
|
quality
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ showPeerInfo &&
|
{ showPeerInfo &&
|
||||||
<div className={classes.peer}>
|
<div className={classes.peer}>
|
||||||
<div className={classes.box}>
|
<div className={classes.box}>
|
||||||
{ isMe ?
|
{ isMe ?
|
||||||
<EditableInput
|
<React.Fragment>
|
||||||
value={displayName}
|
<EditableInput
|
||||||
propName='newDisplayName'
|
value={displayName}
|
||||||
className={classes.displayNameEdit}
|
propName='newDisplayName'
|
||||||
classLoading='loading'
|
className={classes.displayNameEdit}
|
||||||
classInvalid='invalid'
|
classLoading='loading'
|
||||||
shouldBlockWhileLoading
|
classInvalid='invalid'
|
||||||
editProps={{
|
shouldBlockWhileLoading
|
||||||
maxLength : 30,
|
editProps={{
|
||||||
autoCorrect : 'off',
|
maxLength : 30,
|
||||||
spellCheck : false
|
autoCorrect : 'off',
|
||||||
}}
|
spellCheck : false
|
||||||
onChange={({ newDisplayName }) => onChangeDisplayName(newDisplayName)}
|
}}
|
||||||
/>
|
onChange={
|
||||||
|
({ newDisplayName }) =>
|
||||||
|
onChangeDisplayName(newDisplayName)}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
:
|
:
|
||||||
<span className={classes.displayNameStatic}>
|
<span className={classes.displayNameStatic}>
|
||||||
{displayName}
|
{displayName}
|
||||||
|
|
@ -218,7 +392,7 @@ class VideoView extends React.PureComponent
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<video
|
<video
|
||||||
ref='video'
|
ref='videoElement'
|
||||||
className={classnames(classes.video, {
|
className={classnames(classes.video, {
|
||||||
hidden : !videoVisible,
|
hidden : !videoVisible,
|
||||||
'isMe' : isMe && !isScreen,
|
'isMe' : isMe && !isScreen,
|
||||||
|
|
@ -226,6 +400,16 @@ class VideoView extends React.PureComponent
|
||||||
})}
|
})}
|
||||||
autoPlay
|
autoPlay
|
||||||
playsInline
|
playsInline
|
||||||
|
muted
|
||||||
|
controls={false}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<audio
|
||||||
|
ref='audioElement'
|
||||||
|
autoPlay
|
||||||
|
playsInline
|
||||||
|
muted={isMe}
|
||||||
|
controls={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
@ -235,52 +419,87 @@ class VideoView extends React.PureComponent
|
||||||
|
|
||||||
componentDidMount()
|
componentDidMount()
|
||||||
{
|
{
|
||||||
const { videoTrack } = this.props;
|
const { videoTrack, audioTrack } = this.props;
|
||||||
|
|
||||||
this._setTracks(videoTrack);
|
this._setTracks(videoTrack, audioTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount()
|
componentWillUnmount()
|
||||||
{
|
{
|
||||||
clearInterval(this._videoResolutionTimer);
|
clearInterval(this._videoResolutionTimer);
|
||||||
|
|
||||||
|
const { videoElement } = this.refs;
|
||||||
|
|
||||||
|
if (videoElement)
|
||||||
|
{
|
||||||
|
videoElement.oncanplay = null;
|
||||||
|
videoElement.onplay = null;
|
||||||
|
videoElement.onpause = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
componentDidUpdate(prevProps)
|
||||||
UNSAFE_componentWillReceiveProps(nextProps)
|
|
||||||
{
|
{
|
||||||
const { videoTrack } = nextProps;
|
if (prevProps !== this.props)
|
||||||
|
{
|
||||||
this._setTracks(videoTrack);
|
const { videoTrack, audioTrack } = this.props;
|
||||||
|
|
||||||
|
this._setTracks(videoTrack, audioTrack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setTracks(videoTrack)
|
_setTracks(videoTrack, audioTrack)
|
||||||
{
|
{
|
||||||
if (this._videoTrack === videoTrack)
|
if (this._videoTrack === videoTrack && this._audioTrack === audioTrack)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._videoTrack = videoTrack;
|
this._videoTrack = videoTrack;
|
||||||
|
this._audioTrack = audioTrack;
|
||||||
|
|
||||||
clearInterval(this._videoResolutionTimer);
|
clearInterval(this._videoResolutionTimer);
|
||||||
this._hideVideoResolution();
|
this._hideVideoResolution();
|
||||||
|
|
||||||
const { video } = this.refs;
|
const { videoElement, audioElement } = this.refs;
|
||||||
|
|
||||||
if (videoTrack)
|
if (videoTrack)
|
||||||
{
|
{
|
||||||
const stream = new MediaStream();
|
const stream = new MediaStream();
|
||||||
|
|
||||||
if (videoTrack)
|
stream.addTrack(videoTrack);
|
||||||
stream.addTrack(videoTrack);
|
|
||||||
|
|
||||||
video.srcObject = stream;
|
videoElement.srcObject = stream;
|
||||||
|
|
||||||
if (videoTrack)
|
videoElement.oncanplay = () => this.setState({ videoCanPlay: true });
|
||||||
this._showVideoResolution();
|
|
||||||
|
videoElement.onplay = () =>
|
||||||
|
{
|
||||||
|
audioElement.play()
|
||||||
|
.catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
|
||||||
|
};
|
||||||
|
|
||||||
|
videoElement.play()
|
||||||
|
.catch((error) => logger.warn('videoElement.play() [error:"%o]', error));
|
||||||
|
|
||||||
|
this._showVideoResolution();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
video.srcObject = null;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,16 +508,19 @@ class VideoView extends React.PureComponent
|
||||||
this._videoResolutionTimer = setInterval(() =>
|
this._videoResolutionTimer = setInterval(() =>
|
||||||
{
|
{
|
||||||
const { videoWidth, videoHeight } = this.state;
|
const { videoWidth, videoHeight } = this.state;
|
||||||
const { video } = this.refs;
|
const { videoElement } = this.refs;
|
||||||
|
|
||||||
// Don't re-render if nothing changed.
|
// Don't re-render if nothing changed.
|
||||||
if (video.videoWidth === videoWidth && video.videoHeight === videoHeight)
|
if (
|
||||||
|
videoElement.videoWidth === videoWidth &&
|
||||||
|
videoElement.videoHeight === videoHeight
|
||||||
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
videoWidth : video.videoWidth,
|
videoWidth : videoElement.videoWidth,
|
||||||
videoHeight : video.videoHeight
|
videoHeight : videoElement.videoHeight
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
@ -313,11 +535,14 @@ VideoView.propTypes =
|
||||||
{
|
{
|
||||||
isMe : PropTypes.bool,
|
isMe : PropTypes.bool,
|
||||||
isScreen : PropTypes.bool,
|
isScreen : PropTypes.bool,
|
||||||
|
isExtraVideo : PropTypes.bool,
|
||||||
|
showQuality : PropTypes.bool,
|
||||||
displayName : PropTypes.string,
|
displayName : PropTypes.string,
|
||||||
showPeerInfo : PropTypes.bool,
|
showPeerInfo : PropTypes.bool,
|
||||||
videoContain : PropTypes.bool,
|
videoContain : PropTypes.bool,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
videoTrack : PropTypes.any,
|
videoTrack : PropTypes.any,
|
||||||
|
audioTrack : PropTypes.any,
|
||||||
videoVisible : PropTypes.bool.isRequired,
|
videoVisible : PropTypes.bool.isRequired,
|
||||||
consumerSpatialLayers : PropTypes.number,
|
consumerSpatialLayers : PropTypes.number,
|
||||||
consumerTemporalLayers : PropTypes.number,
|
consumerTemporalLayers : PropTypes.number,
|
||||||
|
|
@ -332,7 +557,8 @@ VideoView.propTypes =
|
||||||
videoCodec : PropTypes.string,
|
videoCodec : PropTypes.string,
|
||||||
onChangeDisplayName : PropTypes.func,
|
onChangeDisplayName : PropTypes.func,
|
||||||
children : PropTypes.object,
|
children : PropTypes.object,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired,
|
||||||
|
netInfo : PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(VideoView);
|
export default withStyles(styles)(VideoView);
|
||||||
|
|
|
||||||
|
|
@ -23,18 +23,29 @@ const VideoWindow = (props) =>
|
||||||
!consumer.remotelyPaused
|
!consumer.remotelyPaused
|
||||||
);
|
);
|
||||||
|
|
||||||
let consumerProfile;
|
|
||||||
|
|
||||||
if (consumer)
|
|
||||||
consumerProfile = consumer.profile;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewWindow onUnload={toggleConsumerWindow}>
|
<NewWindow onUnload={toggleConsumerWindow}>
|
||||||
<FullView
|
<FullView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
videoTrack={consumer ? consumer.track : null}
|
consumerSpatialLayers={consumer ? consumer.spatialLayers : null}
|
||||||
|
consumerTemporalLayers={consumer ? consumer.temporalLayers : null}
|
||||||
|
consumerCurrentSpatialLayer={
|
||||||
|
consumer ? consumer.currentSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerCurrentTemporalLayer={
|
||||||
|
consumer ? consumer.currentTemporalLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredSpatialLayer={
|
||||||
|
consumer ? consumer.preferredSpatialLayer : null
|
||||||
|
}
|
||||||
|
consumerPreferredTemporalLayer={
|
||||||
|
consumer ? consumer.preferredTemporalLayer : null
|
||||||
|
}
|
||||||
|
videoMultiLayer={consumer && consumer.type !== 'simple'}
|
||||||
|
videoTrack={consumer && consumer.track}
|
||||||
videoVisible={consumerVisible}
|
videoVisible={consumerVisible}
|
||||||
videoProfile={consumerProfile}
|
videoCodec={consumer && consumer.codec}
|
||||||
|
videoScore={consumer ? consumer.score : null}
|
||||||
/>
|
/>
|
||||||
</NewWindow>
|
</NewWindow>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ export const Me = PropTypes.shape(
|
||||||
export const Producer = PropTypes.shape(
|
export const Producer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
id : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired,
|
||||||
deviceLabel : PropTypes.string,
|
deviceLabel : PropTypes.string,
|
||||||
type : PropTypes.oneOf([ 'front', 'back', 'screen' ]),
|
type : PropTypes.oneOf([ 'front', 'back', 'screen', 'extravideo' ]),
|
||||||
paused : PropTypes.bool.isRequired,
|
paused : PropTypes.bool.isRequired,
|
||||||
track : PropTypes.any,
|
track : PropTypes.any,
|
||||||
codec : PropTypes.string.isRequired
|
codec : PropTypes.string.isRequired
|
||||||
|
|
@ -37,7 +37,7 @@ export const Consumer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
id : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
peerId : PropTypes.string.isRequired,
|
peerId : PropTypes.string.isRequired,
|
||||||
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired,
|
||||||
locallyPaused : PropTypes.bool.isRequired,
|
locallyPaused : PropTypes.bool.isRequired,
|
||||||
remotelyPaused : PropTypes.bool.isRequired,
|
remotelyPaused : PropTypes.bool.isRequired,
|
||||||
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,10 @@ export default function()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
flag,
|
flag,
|
||||||
name : browser.getBrowserName(),
|
os : browser.getOSName(true), // ios, android, linux...
|
||||||
version : browser.getBrowserVersion(),
|
platform : browser.getPlatformType(true), // mobile, desktop, tablet
|
||||||
bowser : browser
|
name : browser.getBrowserName(true),
|
||||||
|
version : browser.getBrowserVersion(),
|
||||||
|
bowser : browser
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.1"
|
|
||||||
id="Layer_1"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 96 96"
|
|
||||||
style="enable-background:new 0 0 96 96;"
|
|
||||||
xml:space="preserve">
|
|
||||||
<metadata
|
|
||||||
id="metadata11"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
|
|
||||||
<defs
|
|
||||||
id="defs9" />
|
|
||||||
<path
|
|
||||||
style="fill:#000000;stroke-width:0.40677965"
|
|
||||||
d="m 33.894283,77.837288 c -1.428534,-1.845763 -3.909722,-5.220659 -5.513751,-7.499764 -1.60403,-2.279109 -4.323663,-5.940126 -6.043631,-8.135593 -5.698554,-7.273973 -6.224902,-8.044795 -6.226676,-9.118803 -0.0034,-2.075799 2.81181,-4.035355 4.9813,-3.467247 0.50339,0.131819 2.562712,1.72771 4.576272,3.546423 4.238418,3.828283 6.617166,5.658035 7.355654,5.658035 0.82497,0 1.045415,-1.364294 0.567453,-3.511881 C 33.348583,54.219654 31.1088,48.20339 28.613609,41.938983 23.524682,29.162764 23.215312,27.731034 25.178629,26.04226 c 2.443255,-2.101599 4.670178,-1.796504 6.362271,0.87165 0.639176,1.007875 2.666245,5.291978 4.504599,9.520229 1.838354,4.228251 3.773553,8.092718 4.300442,8.587705 l 0.957981,0.899977 0.419226,-1.102646 c 0.255274,-0.671424 0.419225,-6.068014 0.419225,-13.799213 0,-13.896836 -0.0078,-13.84873 2.44517,-15.1172 1.970941,-1.019214 4.2259,-0.789449 5.584354,0.569005 l 1.176852,1.176852 0.483523,11.738402 c 0.490017,11.896027 0.826095,14.522982 1.911266,14.939402 1.906224,0.731486 2.21601,-0.184677 4.465407,-13.206045 1.239206,-7.173539 1.968244,-10.420721 2.462128,-10.966454 1.391158,-1.537215 4.742705,-1.519809 6.295208,0.03269 1.147387,1.147388 1.05469,3.124973 -0.669503,14.283063 -0.818745,5.298489 -1.36667,10.090163 -1.220432,10.67282 0.14596,0.581557 0.724796,1.358395 1.286298,1.726306 0.957759,0.627548 1.073422,0.621575 1.86971,-0.09655 0.466837,-0.421011 1.761787,-2.595985 2.877665,-4.833273 2.564176,-5.141059 3.988466,-6.711864 6.085822,-6.711864 2.769954,0 3.610947,2.927256 2.139316,7.446329 C 78.799497,44.318351 66.752066,77.28024 65.51653,80.481356 65.262041,81.140709 64.18139,81.19322 50.866695,81.19322 H 36.491617 Z"
|
|
||||||
id="path3710"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.1"
|
|
||||||
id="Layer_1"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 96 96"
|
|
||||||
style="enable-background:new 0 0 96 96;"
|
|
||||||
xml:space="preserve">
|
|
||||||
<metadata
|
|
||||||
id="metadata11"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
|
|
||||||
<defs
|
|
||||||
id="defs9" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;stroke-width:0.40677965"
|
|
||||||
d="m 33.894283,77.837288 c -1.428534,-1.845763 -3.909722,-5.220659 -5.513751,-7.499764 -1.60403,-2.279109 -4.323663,-5.940126 -6.043631,-8.135593 -5.698554,-7.273973 -6.224902,-8.044795 -6.226676,-9.118803 -0.0034,-2.075799 2.81181,-4.035355 4.9813,-3.467247 0.50339,0.131819 2.562712,1.72771 4.576272,3.546423 4.238418,3.828283 6.617166,5.658035 7.355654,5.658035 0.82497,0 1.045415,-1.364294 0.567453,-3.511881 C 33.348583,54.219654 31.1088,48.20339 28.613609,41.938983 23.524682,29.162764 23.215312,27.731034 25.178629,26.04226 c 2.443255,-2.101599 4.670178,-1.796504 6.362271,0.87165 0.639176,1.007875 2.666245,5.291978 4.504599,9.520229 1.838354,4.228251 3.773553,8.092718 4.300442,8.587705 l 0.957981,0.899977 0.419226,-1.102646 c 0.255274,-0.671424 0.419225,-6.068014 0.419225,-13.799213 0,-13.896836 -0.0078,-13.84873 2.44517,-15.1172 1.970941,-1.019214 4.2259,-0.789449 5.584354,0.569005 l 1.176852,1.176852 0.483523,11.738402 c 0.490017,11.896027 0.826095,14.522982 1.911266,14.939402 1.906224,0.731486 2.21601,-0.184677 4.465407,-13.206045 1.239206,-7.173539 1.968244,-10.420721 2.462128,-10.966454 1.391158,-1.537215 4.742705,-1.519809 6.295208,0.03269 1.147387,1.147388 1.05469,3.124973 -0.669503,14.283063 -0.818745,5.298489 -1.36667,10.090163 -1.220432,10.67282 0.14596,0.581557 0.724796,1.358395 1.286298,1.726306 0.957759,0.627548 1.073422,0.621575 1.86971,-0.09655 0.466837,-0.421011 1.761787,-2.595985 2.877665,-4.833273 2.564176,-5.141059 3.988466,-6.711864 6.085822,-6.711864 2.769954,0 3.610947,2.927256 2.139316,7.446329 C 78.799497,44.318351 66.752066,77.28024 65.51653,80.481356 65.262041,81.140709 64.18139,81.19322 50.866695,81.19322 H 36.491617 Z"
|
|
||||||
id="path3710"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
138
app/src/index.js
138
app/src/index.js
|
|
@ -12,6 +12,7 @@ import RoomClient from './RoomClient';
|
||||||
import RoomContext from './RoomContext';
|
import RoomContext from './RoomContext';
|
||||||
import deviceInfo from './deviceInfo';
|
import deviceInfo from './deviceInfo';
|
||||||
import * as meActions from './actions/meActions';
|
import * as meActions from './actions/meActions';
|
||||||
|
import UnsupportedBrowser from './components/UnsupportedBrowser';
|
||||||
import ChooseRoom from './components/ChooseRoom';
|
import ChooseRoom from './components/ChooseRoom';
|
||||||
import LoadingView from './components/LoadingView';
|
import LoadingView from './components/LoadingView';
|
||||||
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
|
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
|
||||||
|
|
@ -20,7 +21,7 @@ import { persistor, store } from './store';
|
||||||
import { SnackbarProvider } from 'notistack';
|
import { SnackbarProvider } from 'notistack';
|
||||||
import * as serviceWorker from './serviceWorker';
|
import * as serviceWorker from './serviceWorker';
|
||||||
import { ReactLazyPreload } from './components/ReactLazyPreload';
|
import { ReactLazyPreload } from './components/ReactLazyPreload';
|
||||||
|
import { detectDevice } from 'mediasoup-client';
|
||||||
// import messagesEnglish from './translations/en';
|
// import messagesEnglish from './translations/en';
|
||||||
import messagesNorwegian from './translations/nb';
|
import messagesNorwegian from './translations/nb';
|
||||||
import messagesGerman from './translations/de';
|
import messagesGerman from './translations/de';
|
||||||
|
|
@ -31,13 +32,15 @@ import messagesFrench from './translations/fr';
|
||||||
import messagesGreek from './translations/el';
|
import messagesGreek from './translations/el';
|
||||||
import messagesRomanian from './translations/ro';
|
import messagesRomanian from './translations/ro';
|
||||||
import messagesPortuguese from './translations/pt';
|
import messagesPortuguese from './translations/pt';
|
||||||
import messagesChinese from './translations/cn';
|
import messagesChineseSimplified from './translations/cn';
|
||||||
|
import messagesChineseTraditional from './translations/tw';
|
||||||
import messagesSpanish from './translations/es';
|
import messagesSpanish from './translations/es';
|
||||||
import messagesCroatian from './translations/hr';
|
import messagesCroatian from './translations/hr';
|
||||||
import messagesCzech from './translations/cs';
|
import messagesCzech from './translations/cs';
|
||||||
import messagesItalian from './translations/it';
|
import messagesItalian from './translations/it';
|
||||||
import messagesUkrainian from './translations/uk';
|
import messagesUkrainian from './translations/uk';
|
||||||
import messagesTurkish from './translations/tr';
|
import messagesTurkish from './translations/tr';
|
||||||
|
import messagesLatvian from './translations/lv';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
|
|
@ -48,31 +51,57 @@ const cache = createIntlCache();
|
||||||
const messages =
|
const messages =
|
||||||
{
|
{
|
||||||
// 'en' : messagesEnglish,
|
// 'en' : messagesEnglish,
|
||||||
'nb' : messagesNorwegian,
|
'nb' : messagesNorwegian,
|
||||||
'de' : messagesGerman,
|
'de' : messagesGerman,
|
||||||
'hu' : messagesHungarian,
|
'hu' : messagesHungarian,
|
||||||
'pl' : messagesPolish,
|
'pl' : messagesPolish,
|
||||||
'dk' : messagesDanish,
|
'dk' : messagesDanish,
|
||||||
'fr' : messagesFrench,
|
'fr' : messagesFrench,
|
||||||
'el' : messagesGreek,
|
'el' : messagesGreek,
|
||||||
'ro' : messagesRomanian,
|
'ro' : messagesRomanian,
|
||||||
'pt' : messagesPortuguese,
|
'pt' : messagesPortuguese,
|
||||||
'zh' : messagesChinese,
|
'zh-hans' : messagesChineseSimplified,
|
||||||
'es' : messagesSpanish,
|
'zh-hant' : messagesChineseTraditional,
|
||||||
'hr' : messagesCroatian,
|
'es' : messagesSpanish,
|
||||||
'cs' : messagesCzech,
|
'hr' : messagesCroatian,
|
||||||
'it' : messagesItalian,
|
'cs' : messagesCzech,
|
||||||
'uk' : messagesUkrainian,
|
'it' : messagesItalian,
|
||||||
'tr' : messagesTurkish
|
'uk' : messagesUkrainian,
|
||||||
|
'tr' : messagesTurkish,
|
||||||
|
'lv' : messagesLatvian
|
||||||
};
|
};
|
||||||
|
|
||||||
const locale = navigator.language.split(/[-_]/)[0]; // language without region code
|
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 intl = createIntl({
|
const intl = createIntl({
|
||||||
locale,
|
locale,
|
||||||
messages : messages[locale]
|
messages : messages[locale]
|
||||||
}, cache);
|
}, cache);
|
||||||
|
|
||||||
|
document.documentElement.lang = locale;
|
||||||
|
|
||||||
if (process.env.REACT_APP_DEBUG === '*' || process.env.NODE_ENV !== 'production')
|
if (process.env.REACT_APP_DEBUG === '*' || process.env.NODE_ENV !== 'production')
|
||||||
{
|
{
|
||||||
debug.enable('* -engine* -socket* -RIE* *WARN* *ERROR*');
|
debug.enable('* -engine* -socket* -RIE* *WARN* *ERROR*');
|
||||||
|
|
@ -110,15 +139,75 @@ function run()
|
||||||
|
|
||||||
const accessCode = parameters.get('code');
|
const accessCode = parameters.get('code');
|
||||||
const produce = parameters.get('produce') !== 'false';
|
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 forceTcp = parameters.get('forceTcp') === 'true';
|
||||||
const displayName = parameters.get('displayName');
|
const displayName = parameters.get('displayName');
|
||||||
const muted = parameters.get('muted') === 'true';
|
const muted = parameters.get('muted') === 'true';
|
||||||
|
|
||||||
|
const { pathname } = window.location;
|
||||||
|
|
||||||
|
let basePath = pathname.substring(0, pathname.lastIndexOf('/'));
|
||||||
|
|
||||||
|
if (!basePath)
|
||||||
|
basePath = '/';
|
||||||
|
|
||||||
// Get current device.
|
// Get current device.
|
||||||
const device = deviceInfo();
|
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(
|
store.dispatch(
|
||||||
meActions.setMe({
|
meActions.setMe({
|
||||||
peerId,
|
peerId,
|
||||||
|
|
@ -131,12 +220,11 @@ function run()
|
||||||
peerId,
|
peerId,
|
||||||
accessCode,
|
accessCode,
|
||||||
device,
|
device,
|
||||||
useSimulcast,
|
|
||||||
useSharingSimulcast,
|
|
||||||
produce,
|
produce,
|
||||||
forceTcp,
|
forceTcp,
|
||||||
displayName,
|
displayName,
|
||||||
muted
|
muted,
|
||||||
|
basePath
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CLIENT = roomClient;
|
global.CLIENT = roomClient;
|
||||||
|
|
@ -148,7 +236,7 @@ function run()
|
||||||
<PersistGate loading={<LoadingView />} persistor={persistor}>
|
<PersistGate loading={<LoadingView />} persistor={persistor}>
|
||||||
<RoomContext.Provider value={roomClient}>
|
<RoomContext.Provider value={roomClient}>
|
||||||
<SnackbarProvider>
|
<SnackbarProvider>
|
||||||
<Router>
|
<Router basename={basePath}>
|
||||||
<Suspense fallback={<LoadingView />}>
|
<Suspense fallback={<LoadingView />}>
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Route exact path='/' component={ChooseRoom} />
|
<Route exact path='/' component={ChooseRoom} />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
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'
|
||||||
|
};
|
||||||
|
|
@ -30,6 +30,11 @@ const chat = (state = [], action) =>
|
||||||
return [ ...state, ...chatHistory ];
|
return [ ...state, ...chatHistory ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'CLEAR_CHAT':
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,11 @@ const consumers = (state = initialState, action) =>
|
||||||
return { ...state, [consumerId]: newConsumer };
|
return { ...state, [consumerId]: newConsumer };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'CLEAR_CONSUMERS':
|
||||||
|
{
|
||||||
|
return initialState;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,9 @@ const files = (state = {}, action) =>
|
||||||
return { ...state, [magnetUri]: newFile };
|
return { ...state, [magnetUri]: newFile };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'CLEAR_FILES':
|
||||||
|
return {};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ const initialState =
|
||||||
{
|
{
|
||||||
id : null,
|
id : null,
|
||||||
picture : null,
|
picture : null,
|
||||||
|
browser : null,
|
||||||
|
roles : [ 'normal' ], // Default role
|
||||||
canSendMic : false,
|
canSendMic : false,
|
||||||
canSendWebcam : false,
|
canSendWebcam : false,
|
||||||
canShareScreen : false,
|
canShareScreen : false,
|
||||||
|
|
@ -13,9 +15,11 @@ const initialState =
|
||||||
screenShareInProgress : false,
|
screenShareInProgress : false,
|
||||||
displayNameInProgress : false,
|
displayNameInProgress : false,
|
||||||
loginEnabled : false,
|
loginEnabled : false,
|
||||||
raiseHand : false,
|
raisedHand : false,
|
||||||
raiseHandInProgress : false,
|
raisedHandInProgress : false,
|
||||||
loggedIn : false
|
loggedIn : false,
|
||||||
|
isSpeaking : false,
|
||||||
|
isAutoMuted : true
|
||||||
};
|
};
|
||||||
|
|
||||||
const me = (state = initialState, action) =>
|
const me = (state = initialState, action) =>
|
||||||
|
|
@ -36,6 +40,13 @@ const me = (state = initialState, action) =>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_BROWSER':
|
||||||
|
{
|
||||||
|
const { browser } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, browser };
|
||||||
|
}
|
||||||
|
|
||||||
case 'LOGGED_IN':
|
case 'LOGGED_IN':
|
||||||
{
|
{
|
||||||
const { flag } = action.payload;
|
const { flag } = action.payload;
|
||||||
|
|
@ -43,6 +54,24 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, loggedIn: flag };
|
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':
|
case 'SET_PICTURE':
|
||||||
return { ...state, picture: action.payload.picture };
|
return { ...state, picture: action.payload.picture };
|
||||||
|
|
||||||
|
|
@ -71,6 +100,13 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, audioDevices: devices };
|
return { ...state, audioDevices: devices };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_AUDIO_OUTPUT_DEVICES':
|
||||||
|
{
|
||||||
|
const { devices } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, audioOutputDevices: devices };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_WEBCAM_DEVICES':
|
case 'SET_WEBCAM_DEVICES':
|
||||||
{
|
{
|
||||||
const { devices } = action.payload;
|
const { devices } = action.payload;
|
||||||
|
|
@ -99,18 +135,18 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, screenShareInProgress: flag };
|
return { ...state, screenShareInProgress: flag };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_MY_RAISE_HAND_STATE':
|
case 'SET_RAISED_HAND':
|
||||||
{
|
{
|
||||||
const { flag } = action.payload;
|
const { flag } = action.payload;
|
||||||
|
|
||||||
return { ...state, raiseHand: flag };
|
return { ...state, raisedHand: flag };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_MY_RAISE_HAND_STATE_IN_PROGRESS':
|
case 'SET_RAISED_HAND_IN_PROGRESS':
|
||||||
{
|
{
|
||||||
const { flag } = action.payload;
|
const { flag } = action.payload;
|
||||||
|
|
||||||
return { ...state, raiseHandInProgress: flag };
|
return { ...state, raisedHandInProgress: flag };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_DISPLAY_NAME_IN_PROGRESS':
|
case 'SET_DISPLAY_NAME_IN_PROGRESS':
|
||||||
|
|
@ -120,6 +156,20 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, displayNameInProgress: flag };
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ const peerVolumes = (state = initialState, action) =>
|
||||||
peerId
|
peerId
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
|
|
||||||
return { ...state, [peerId]: 0 };
|
return { ...state, [peerId]: -100 };
|
||||||
}
|
}
|
||||||
case 'ADD_PEER':
|
case 'ADD_PEER':
|
||||||
{
|
{
|
||||||
const { peer } = action.payload;
|
const { peer } = action.payload;
|
||||||
|
|
||||||
return { ...state, [peer.id]: 0 };
|
return { ...state, [peer.id]: -100 };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'REMOVE_PEER':
|
case 'REMOVE_PEER':
|
||||||
|
|
@ -31,9 +31,10 @@ const peerVolumes = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'SET_PEER_VOLUME':
|
case 'SET_PEER_VOLUME':
|
||||||
{
|
{
|
||||||
const { peerId, volume } = action.payload;
|
const { peerId } = action.payload;
|
||||||
|
const dBs = action.payload.volume < -100 ? -100 : action.payload.volume;
|
||||||
|
|
||||||
return { ...state, [peerId]: volume };
|
return { ...state, [peerId]: Math.round(dBs) };
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
const peer = (state = {}, action) =>
|
const initialState = {};
|
||||||
|
|
||||||
|
const peer = (state = initialState, action) =>
|
||||||
{
|
{
|
||||||
switch (action.type)
|
switch (action.type)
|
||||||
{
|
{
|
||||||
|
|
@ -17,8 +19,21 @@ const peer = (state = {}, action) =>
|
||||||
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
||||||
return { ...state, peerScreenInProgress: action.payload.flag };
|
return { ...state, peerScreenInProgress: action.payload.flag };
|
||||||
|
|
||||||
case 'SET_PEER_RAISE_HAND_STATE':
|
case 'SET_PEER_KICK_IN_PROGRESS':
|
||||||
return { ...state, raiseHandState: action.payload.raiseHandState };
|
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 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
{
|
{
|
||||||
|
|
@ -40,12 +55,44 @@ const peer = (state = {}, action) =>
|
||||||
return { ...state, picture: action.payload.picture };
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const peers = (state = {}, action) =>
|
const peers = (state = initialState, action) =>
|
||||||
{
|
{
|
||||||
switch (action.type)
|
switch (action.type)
|
||||||
{
|
{
|
||||||
|
|
@ -68,9 +115,15 @@ const peers = (state = {}, action) =>
|
||||||
case 'SET_PEER_VIDEO_IN_PROGRESS':
|
case 'SET_PEER_VIDEO_IN_PROGRESS':
|
||||||
case 'SET_PEER_AUDIO_IN_PROGRESS':
|
case 'SET_PEER_AUDIO_IN_PROGRESS':
|
||||||
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
||||||
case 'SET_PEER_RAISE_HAND_STATE':
|
case 'SET_PEER_RAISED_HAND':
|
||||||
|
case 'SET_PEER_RAISED_HAND_IN_PROGRESS':
|
||||||
case 'SET_PEER_PICTURE':
|
case 'SET_PEER_PICTURE':
|
||||||
case 'ADD_CONSUMER':
|
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];
|
const oldPeer = state[action.payload.peerId];
|
||||||
|
|
||||||
|
|
@ -82,6 +135,7 @@ const peers = (state = {}, action) =>
|
||||||
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_PEER_KICK_IN_PROGRESS':
|
||||||
case 'REMOVE_CONSUMER':
|
case 'REMOVE_CONSUMER':
|
||||||
{
|
{
|
||||||
const oldPeer = state[action.payload.peerId];
|
const oldPeer = state[action.payload.peerId];
|
||||||
|
|
@ -93,6 +147,11 @@ const peers = (state = {}, action) =>
|
||||||
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'CLEAR_PEERS':
|
||||||
|
{
|
||||||
|
return initialState;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,17 @@ const producers = (state = initialState, action) =>
|
||||||
return { ...state, [producerId]: newProducer };
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,40 @@
|
||||||
const initialState =
|
const initialState =
|
||||||
{
|
{
|
||||||
name : '',
|
name : '',
|
||||||
state : 'new', // new/connecting/connected/disconnected/closed,
|
// new/connecting/connected/disconnected/closed,
|
||||||
locked : false,
|
state : 'new',
|
||||||
inLobby : false,
|
locked : false,
|
||||||
signInRequired : false,
|
inLobby : false,
|
||||||
accessCode : '', // access code to the room if locked and joinByAccessCode == true
|
signInRequired : false,
|
||||||
joinByAccessCode : true, // if true: accessCode is a possibility to open the room
|
overRoomLimit : false,
|
||||||
activeSpeakerId : null,
|
// access code to the room if locked and joinByAccessCode == true
|
||||||
torrentSupport : false,
|
accessCode : '',
|
||||||
showSettings : false,
|
// if true: accessCode is a possibility to open the room
|
||||||
fullScreenConsumer : null, // ConsumerID
|
joinByAccessCode : true,
|
||||||
windowConsumer : null, // ConsumerID
|
activeSpeakerId : null,
|
||||||
toolbarsVisible : true,
|
torrentSupport : false,
|
||||||
mode : 'democratic',
|
showSettings : false,
|
||||||
selectedPeerId : null,
|
fullScreenConsumer : null, // ConsumerID
|
||||||
spotlights : [],
|
windowConsumer : null, // ConsumerID
|
||||||
settingsOpen : false,
|
toolbarsVisible : true,
|
||||||
lockDialogOpen : false,
|
mode : window.config.defaultLayout || 'democratic',
|
||||||
joined : false
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
const room = (state = initialState, action) =>
|
const room = (state = initialState, action) =>
|
||||||
|
|
@ -65,7 +81,12 @@ const room = (state = initialState, action) =>
|
||||||
|
|
||||||
return { ...state, signInRequired };
|
return { ...state, signInRequired };
|
||||||
}
|
}
|
||||||
|
case 'SET_OVER_ROOM_LIMIT':
|
||||||
|
{
|
||||||
|
const { overRoomLimit } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, overRoomLimit };
|
||||||
|
}
|
||||||
case 'SET_ACCESS_CODE':
|
case 'SET_ACCESS_CODE':
|
||||||
{
|
{
|
||||||
const { accessCode } = action.payload;
|
const { accessCode } = action.payload;
|
||||||
|
|
@ -94,6 +115,34 @@ const room = (state = initialState, action) =>
|
||||||
return { ...state, settingsOpen };
|
return { ...state, settingsOpen };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_EXTRA_VIDEO_OPEN':
|
||||||
|
{
|
||||||
|
const { extraVideoOpen } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, extraVideoOpen };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_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':
|
case 'SET_ROOM_ACTIVE_SPEAKER':
|
||||||
{
|
{
|
||||||
const { peerId } = action.payload;
|
const { peerId } = action.payload;
|
||||||
|
|
@ -110,7 +159,7 @@ const room = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'TOGGLE_JOINED':
|
case 'TOGGLE_JOINED':
|
||||||
{
|
{
|
||||||
const joined = !state.joined;
|
const joined = true;
|
||||||
|
|
||||||
return { ...state, joined };
|
return { ...state, joined };
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +212,46 @@ const room = (state = initialState, action) =>
|
||||||
return { ...state, spotlights };
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,16 @@ import chat from './chat';
|
||||||
import toolarea from './toolarea';
|
import toolarea from './toolarea';
|
||||||
import files from './files';
|
import files from './files';
|
||||||
import settings from './settings';
|
import settings from './settings';
|
||||||
|
import transports from './transports';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
room,
|
room,
|
||||||
me,
|
me,
|
||||||
producers,
|
producers,
|
||||||
|
consumers,
|
||||||
|
transports,
|
||||||
peers,
|
peers,
|
||||||
lobbyPeers,
|
lobbyPeers,
|
||||||
consumers,
|
|
||||||
peerVolumes,
|
peerVolumes,
|
||||||
notifications,
|
notifications,
|
||||||
chat,
|
chat,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,31 @@
|
||||||
const initialState =
|
const initialState =
|
||||||
{
|
{
|
||||||
displayName : 'Guest',
|
displayName : 'Guest',
|
||||||
selectedWebcam : null,
|
selectedWebcam : null,
|
||||||
selectedAudioDevice : null,
|
selectedAudioDevice : null,
|
||||||
advancedMode : false,
|
advancedMode : false,
|
||||||
resolution : 'medium', // low, medium, high, veryhigh, ultra
|
sampleRate : 48000,
|
||||||
lastN : 4,
|
channelCount : 1,
|
||||||
permanentTopBar : true
|
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,
|
||||||
|
lastN : 4,
|
||||||
|
permanentTopBar : true,
|
||||||
|
hiddenControls : false,
|
||||||
|
showNotifications : true,
|
||||||
|
notificationSounds : true,
|
||||||
|
buttonControlBar : window.config.buttonControlBar || false,
|
||||||
|
drawerOverlayed : window.config.drawerOverlayed || true,
|
||||||
|
...window.config.defaultAudio
|
||||||
};
|
};
|
||||||
|
|
||||||
const settings = (state = initialState, action) =>
|
const settings = (state = initialState, action) =>
|
||||||
|
|
@ -23,6 +42,11 @@ const settings = (state = initialState, action) =>
|
||||||
return { ...state, selectedAudioDevice: action.payload.deviceId };
|
return { ...state, selectedAudioDevice: action.payload.deviceId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'CHANGE_AUDIO_OUTPUT_DEVICE':
|
||||||
|
{
|
||||||
|
return { ...state, selectedAudioOutputDevice: action.payload.deviceId };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_DISPLAY_NAME':
|
case 'SET_DISPLAY_NAME':
|
||||||
{
|
{
|
||||||
const { displayName } = action.payload;
|
const { displayName } = action.payload;
|
||||||
|
|
@ -37,6 +61,76 @@ const settings = (state = initialState, action) =>
|
||||||
return { ...state, advancedMode };
|
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':
|
case 'SET_LAST_N':
|
||||||
{
|
{
|
||||||
const { lastN } = action.payload;
|
const { lastN } = action.payload;
|
||||||
|
|
@ -51,6 +145,41 @@ const settings = (state = initialState, action) =>
|
||||||
return { ...state, permanentTopBar };
|
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':
|
case 'SET_VIDEO_RESOLUTION':
|
||||||
{
|
{
|
||||||
const { resolution } = action.payload;
|
const { resolution } = action.payload;
|
||||||
|
|
@ -58,6 +187,27 @@ const settings = (state = initialState, action) =>
|
||||||
return { ...state, resolution };
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
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;
|
||||||
|
|
@ -49,6 +49,26 @@
|
||||||
"room.spotlights": "Spotlight中的参与者",
|
"room.spotlights": "Spotlight中的参与者",
|
||||||
"room.passive": "被动参与者",
|
"room.passive": "被动参与者",
|
||||||
"room.videoPaused": "该视频已暂停",
|
"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.login": "登录",
|
||||||
"tooltip.logout": "注销",
|
"tooltip.logout": "注销",
|
||||||
|
|
@ -60,6 +80,15 @@
|
||||||
"tooltip.lobby": "显示大厅",
|
"tooltip.lobby": "显示大厅",
|
||||||
"tooltip.settings": "显示设置",
|
"tooltip.settings": "显示设置",
|
||||||
"tooltip.participants": "显示参加者",
|
"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.roomName": "房间名称",
|
||||||
"label.chooseRoomButton": "继续",
|
"label.chooseRoomButton": "继续",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "文件共享",
|
"label.filesharing": "文件共享",
|
||||||
"label.participants": "参与者",
|
"label.participants": "参与者",
|
||||||
"label.shareFile": "共享文件",
|
"label.shareFile": "共享文件",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "不支持文件共享",
|
"label.fileSharingUnsupported": "不支持文件共享",
|
||||||
"label.unknown": "未知",
|
"label.unknown": "未知",
|
||||||
"label.democratic": "民主视图",
|
"label.democratic": "民主视图",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "非常高 (FHD)",
|
"label.veryHigh": "非常高 (FHD)",
|
||||||
"label.ultra": "超高 (UHD)",
|
"label.ultra": "超高 (UHD)",
|
||||||
"label.close": "关闭",
|
"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.settings": "设置",
|
||||||
"settings.camera": "视频设备",
|
"settings.camera": "视频设备",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "音频设备",
|
"settings.audio": "音频设备",
|
||||||
"settings.selectAudio": "选择音频设备",
|
"settings.selectAudio": "选择音频设备",
|
||||||
"settings.cantSelectAudio": "无法选择音频设备",
|
"settings.cantSelectAudio": "无法选择音频设备",
|
||||||
|
"settings.audioOutput": "音频输出设备",
|
||||||
|
"settings.selectAudioOutput": "选择音频输出设备",
|
||||||
|
"settings.cantSelectAudioOutput": "无法选择音频输出设备",
|
||||||
"settings.resolution": "选择视频分辨率",
|
"settings.resolution": "选择视频分辨率",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "房间布局",
|
"settings.layout": "房间布局",
|
||||||
"settings.selectRoomLayout": "选择房间布局",
|
"settings.selectRoomLayout": "选择房间布局",
|
||||||
"settings.advancedMode": "高级模式",
|
"settings.advancedMode": "高级模式",
|
||||||
"settings.permanentTopBar": "永久顶吧",
|
"settings.permanentTopBar": "永久顶吧",
|
||||||
"settings.lastn": "可见视频数量",
|
"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.saveFileError": "无法保存文件",
|
||||||
"filesharing.startingFileShare": "正在尝试共享文件",
|
"filesharing.startingFileShare": "正在尝试共享文件",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "麦克风已断开",
|
"devices.microphoneDisconnected": "麦克风已断开",
|
||||||
"devices.microphoneError": "麦克风发生错误",
|
"devices.microphoneError": "麦克风发生错误",
|
||||||
"devices.microPhoneMute": "麦克风静音",
|
"devices.microphoneMute": "麦克风静音",
|
||||||
"devices.micophoneUnMute": "取消麦克风静音",
|
"devices.microphoneUnMute": "取消麦克风静音",
|
||||||
"devices.microphoneEnable": "启用了麦克风",
|
"devices.microphoneEnable": "启用了麦克风",
|
||||||
"devices.microphoneMuteError": "无法使麦克风静音",
|
"devices.microphoneMuteError": "无法使麦克风静音",
|
||||||
"devices.microphoneUnMuteError": "无法取消麦克风静音",
|
"devices.microphoneUnMuteError": "无法取消麦克风静音",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "访问屏幕时发生错误",
|
"devices.screenSharingError": "访问屏幕时发生错误",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "相机已断开连接",
|
"devices.cameraDisconnected": "相机已断开连接",
|
||||||
"devices.cameraError": "访问相机时发生错误"
|
"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
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +48,27 @@
|
||||||
"room.spotlights": "Aktivní Účastníci",
|
"room.spotlights": "Aktivní Účastníci",
|
||||||
"room.passive": "Pasivní Účastníci",
|
"room.passive": "Pasivní Účastníci",
|
||||||
"room.videoPaused": "Toto video bylo pozastaveno",
|
"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.login": "Přihlášení",
|
||||||
"tooltip.logout": "Odhlášení",
|
"tooltip.logout": "Odhlášení",
|
||||||
|
|
@ -58,6 +79,15 @@
|
||||||
"tooltip.leaveFullscreen": "Vypnout režim celé obrazovky (fullscreen)",
|
"tooltip.leaveFullscreen": "Vypnout režim celé obrazovky (fullscreen)",
|
||||||
"tooltip.lobby": "Ukázat Přijímací místnost",
|
"tooltip.lobby": "Ukázat Přijímací místnost",
|
||||||
"tooltip.settings": "Zobrazit nastavení",
|
"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.roomName": "Jméno místnosti",
|
||||||
"label.chooseRoomButton": "Pokračovat",
|
"label.chooseRoomButton": "Pokračovat",
|
||||||
|
|
@ -71,6 +101,7 @@
|
||||||
"label.filesharing": "Sdílení souborů",
|
"label.filesharing": "Sdílení souborů",
|
||||||
"label.participants": "Účastníci",
|
"label.participants": "Účastníci",
|
||||||
"label.shareFile": "Sdílet soubor",
|
"label.shareFile": "Sdílet soubor",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "Sdílení souborů není podporováno",
|
"label.fileSharingUnsupported": "Sdílení souborů není podporováno",
|
||||||
"label.unknown": "Neznámý",
|
"label.unknown": "Neznámý",
|
||||||
"label.democratic": "Rozvržení: Demokratické",
|
"label.democratic": "Rozvržení: Demokratické",
|
||||||
|
|
@ -81,6 +112,13 @@
|
||||||
"label.veryHigh": "Velmi vysoké (FHD)",
|
"label.veryHigh": "Velmi vysoké (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Zavřít",
|
"label.close": "Zavřít",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearance": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
"label.promoteAllPeers": null,
|
||||||
|
"label.moreActions": null,
|
||||||
|
"label.version": null,
|
||||||
|
|
||||||
"settings.settings": "Nastavení",
|
"settings.settings": "Nastavení",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -89,10 +127,30 @@
|
||||||
"settings.audio": "Audio zařízení",
|
"settings.audio": "Audio zařízení",
|
||||||
"settings.selectAudio": "Vyberte audio zařízení",
|
"settings.selectAudio": "Vyberte audio zařízení",
|
||||||
"settings.cantSelectAudio": "Není možno vybrat 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.resolution": "Vyberte rozlišení vašeho videa",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Rozvržení místnosti",
|
"settings.layout": "Rozvržení místnosti",
|
||||||
"settings.selectRoomLayout": "Vyberte rozvržení místnosti",
|
"settings.selectRoomLayout": "Vyberte rozvržení místnosti",
|
||||||
"settings.advancedMode": "Pokočilý mód",
|
"settings.advancedMode": "Pokočilý mód",
|
||||||
|
"settings.permanentTopBar": null,
|
||||||
|
"settings.lastn": null,
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
"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.saveFileError": "Není možné uložit soubor",
|
||||||
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
|
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
|
||||||
|
|
@ -122,8 +180,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Mikrofon odpojen",
|
"devices.microphoneDisconnected": "Mikrofon odpojen",
|
||||||
"devices.microphoneError": "Při přístupu k vašemu mikrofonu se vyskytla chyba",
|
"devices.microphoneError": "Při přístupu k vašemu mikrofonu se vyskytla chyba",
|
||||||
"devices.microPhoneMute": "Mikrofon ztišen",
|
"devices.microphoneMute": "Mikrofon ztišen",
|
||||||
"devices.micophoneUnMute": "Ztišení mikrofonu zrušeno",
|
"devices.microphoneUnMute": "Ztišení mikrofonu zrušeno",
|
||||||
"devices.microphoneEnable": "Mikrofon povolen",
|
"devices.microphoneEnable": "Mikrofon povolen",
|
||||||
"devices.microphoneMuteError": "Není možné ztišit váš mikrofon",
|
"devices.microphoneMuteError": "Není možné ztišit váš mikrofon",
|
||||||
"devices.microphoneUnMuteError": "Není možné zrušit ztišení vašeho mikrofonu",
|
"devices.microphoneUnMuteError": "Není možné zrušit ztišení vašeho mikrofonu",
|
||||||
|
|
@ -132,5 +190,15 @@
|
||||||
"devices.screenSharingError": "Při přístupu k vaší obrazovce se vyskytla chyba",
|
"devices.screenSharingError": "Při přístupu k vaší obrazovce se vyskytla chyba",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Kamera odpojena",
|
"devices.cameraDisconnected": "Kamera odpojena",
|
||||||
"devices.cameraError": "Při přístupu k vaší kameře se vyskytla chyba"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,46 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected": "Verbindung unterbrochen",
|
"socket.disconnected": "Verbindung unterbrochen",
|
||||||
"socket.reconnecting": "Verbindung unterbrochen, versuche neu zu verbinden",
|
"socket.reconnecting": "Verbindung unterbrochen, versuche neu zu verbinden",
|
||||||
"socket.reconnected": "Verbindung wieder herges|tellt",
|
"socket.reconnected": "Verbindung wiederhergestellt",
|
||||||
"socket.requestError": "Fehler bei Serveranfrage",
|
"socket.requestError": "Fehler bei Serveranfrage",
|
||||||
|
|
||||||
"room.chooseRoom": "Wähle den Namen für den Raum, den du betreten möchtest",
|
"room.chooseRoom": "Wähle den Raum aus, den du betreten möchtest",
|
||||||
"room.cookieConsent": "Diese Seite verwendet Cookies, um die Benutzerfreundlichkeit zu erhöhen",
|
"room.cookieConsent": "Diese Seite verwendet Cookies, um die Benutzerfreundlichkeit zu erhöhen",
|
||||||
"room.consentUnderstand": "Ich stimme zu",
|
"room.consentUnderstand": "Verstanden",
|
||||||
"room.joined": "Konferenzraum betreten",
|
"room.joined": "Du bist dem Raum beigetreten",
|
||||||
"room.cantJoin": "Betreten des Raumes nicht möglich",
|
"room.cantJoin": "Betreten des Raumes nicht möglich",
|
||||||
"room.youLocked": "Raum wurde abgeschlossen",
|
"room.youLocked": "Du hast den Raum abgeschlossen",
|
||||||
"room.cantLock": "Abschließen des Raumes nicht möglich",
|
"room.cantLock": "Abschließen des Raumes nicht möglich",
|
||||||
"room.youUnLocked": "Raum geöffnet",
|
"room.youUnLocked": "Du hast den Raum geöffnet",
|
||||||
"room.cantUnLock": "Öffnen des Raumes nicht möglich",
|
"room.cantUnLock": "Öffnen des Raumes nicht möglich",
|
||||||
"room.locked": "Raum wurde abgeschlossen",
|
"room.locked": "Raum wurde abgeschlossen",
|
||||||
"room.unlocked": "Raum wurde geöffnet",
|
"room.unlocked": "Raum wurde geöffnet",
|
||||||
"room.newLobbyPeer": "Neuer Teilnehmer im Empfangsraum",
|
"room.newLobbyPeer": "Neuer Teilnehmer im Warteraum",
|
||||||
"room.lobbyPeerLeft": "Teilnehmer hat Empfangsraum verlassen",
|
"room.lobbyPeerLeft": "Ein Teilnehmer hat den Warteraum verlassen",
|
||||||
"room.lobbyPeerChangedDisplayName": "Teilnehmer im Empfangsraum hat seinen Namen geändert: {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Ein Teilnehmer im Warteraum hat seinen Namen geändert zu: {displayName}",
|
||||||
"room.lobbyPeerChangedPicture": "Teilnehmer in Empfangsraum hat sein Avatar geändert",
|
"room.lobbyPeerChangedPicture": "Ein Teilnehmer im Warteraum hat seinen Avatar geändert",
|
||||||
"room.setAccessCode": "Zugangskode für den Raum geändert",
|
"room.setAccessCode": "Zugangscode für den Raum geändert",
|
||||||
"room.accessCodeOn": "Zugangskode aktiviert",
|
"room.accessCodeOn": "Zugangscode aktiviert",
|
||||||
"room.accessCodeOff": "Zugangskode deaktiviert",
|
"room.accessCodeOff": "Zugangscode deaktiviert",
|
||||||
"room.peerChangedDisplayName": "{oldDisplayName} heißt jetzt {displayName}",
|
"room.peerChangedDisplayName": "{oldDisplayName} heißt jetzt {displayName}",
|
||||||
"room.newPeer": "{displayName} hat den Raum betreten",
|
"room.newPeer": "{displayName} hat den Raum betreten",
|
||||||
"room.newFile": "Neue Datei verfügbar",
|
"room.newFile": "Neue Datei verfügbar",
|
||||||
"room.toggleAdvancedMode": "Erweiterter Modus aktiv",
|
"room.toggleAdvancedMode": "Erweiterter Modus aktiv",
|
||||||
"room.setDemocraticView": "Raumlayout demokratisch",
|
"room.setDemocraticView": "Demokratische Ansicht",
|
||||||
"room.setFilmStripView": "Raumlayout Filmstreifen",
|
"room.setFilmStripView": "Filmstreifen-Ansicht",
|
||||||
"room.loggedIn": "Angemeldet",
|
"room.loggedIn": "Angemeldet",
|
||||||
"room.loggedOut": "Abgemeldet",
|
"room.loggedOut": "Abgemeldet",
|
||||||
"room.changedDisplayName": "Dein Name ist jetzt {displayName}",
|
"room.changedDisplayName": "Dein Name ist jetzt {displayName}",
|
||||||
"room.changeDisplayNameError": "Konnte Name nicht ändern",
|
"room.changeDisplayNameError": "Dein Name konnte nicht geändert werden",
|
||||||
"room.chatError": "Konnte Meldung nicht senden",
|
"room.chatError": "Die Chat-Nachricht konnte nicht gesendet werden",
|
||||||
"room.aboutToJoin": "Du bist dabei den Raum zu betreten",
|
"room.aboutToJoin": "Du bist dabei, folgenden Raum zu betreten:",
|
||||||
"room.roomId": "Raum ID: {roomName}",
|
"room.roomId": "Raum ID: {roomName}",
|
||||||
"room.setYourName": "Gib deinen Namen an und wähle wie den Raum betreten willst",
|
"room.setYourName": "Gib deinen Namen an und wähle aus, wie du den Raum betreten willst:",
|
||||||
"room.audioOnly": "Nur Audio",
|
"room.audioOnly": "Nur Audio",
|
||||||
"room.audioVideo": "Audio und Video",
|
"room.audioVideo": "Audio und Video",
|
||||||
"room.youAreReady": "Ok, Du bist bereit",
|
"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.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 Dir jemand öffnet",
|
"room.locketWait": "Der Raum ist abgeschlossen - warte, bis dich jemand rein lässt...",
|
||||||
"room.lobbyAdministration": "Warteraum",
|
"room.lobbyAdministration": "Warteraum",
|
||||||
"room.peersInLobby": "Teilnehmer im Warteraum",
|
"room.peersInLobby": "Teilnehmer im Warteraum",
|
||||||
"room.lobbyEmpty": "Der Warteraum ist leer",
|
"room.lobbyEmpty": "Der Warteraum ist leer",
|
||||||
|
|
@ -49,92 +49,157 @@
|
||||||
"room.spotlights": "Aktive Teinehmer",
|
"room.spotlights": "Aktive Teinehmer",
|
||||||
"room.passive": "Passive Teilnehmer",
|
"room.passive": "Passive Teilnehmer",
|
||||||
"room.videoPaused": "Video gestoppt",
|
"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.login": "Anmelden",
|
||||||
"tooltip.logout": "Abmelden",
|
"tooltip.logout": "Abmelden",
|
||||||
"tooltip.admitFromLobby": "Teilnehmer aktivieren",
|
"tooltip.admitFromLobby": "Teilnehmer reinlassen",
|
||||||
"tooltip.lockRoom": "Raum abschließen",
|
"tooltip.lockRoom": "Raum abschließen",
|
||||||
"tooltip.unLockRoom": "Raum öffnen",
|
"tooltip.unLockRoom": "Raum entsperren",
|
||||||
"tooltip.enterFullscreen": "Vollbild",
|
"tooltip.enterFullscreen": "Vollbild",
|
||||||
"tooltip.leaveFullscreen": "Vollbild verlassen",
|
"tooltip.leaveFullscreen": "Vollbild verlassen",
|
||||||
"tooltip.lobby": "Warteraum",
|
"tooltip.lobby": "Warteraum",
|
||||||
"tooltip.settings": "Einstellungen",
|
"tooltip.settings": "Einstellungen",
|
||||||
"tooltip.participants": "Teilnehmer",
|
"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 Raumes",
|
"label.roomName": "Name des Raums",
|
||||||
"label.chooseRoomButton": "Weiter",
|
"label.chooseRoomButton": "Weiter",
|
||||||
"label.yourName": "Dein Name",
|
"label.yourName": "Dein Name",
|
||||||
"label.newWindow": "In separatem Fenster öffnen",
|
"label.newWindow": "Neues Fenster",
|
||||||
"label.fullscreen": "Vollbild",
|
"label.fullscreen": "Vollbild",
|
||||||
"label.openDrawer": "Menü",
|
"label.openDrawer": "Menü",
|
||||||
"label.leave": "Ausgang",
|
"label.leave": "Verlassen",
|
||||||
"label.chatInput": "Schreibe Chat...",
|
"label.chatInput": "Schreibe eine Nachricht...",
|
||||||
"label.chat": "Chat",
|
"label.chat": "Chat",
|
||||||
"label.filesharing": "Dateien",
|
"label.filesharing": "Dateien",
|
||||||
"label.participants": "Teilnehmer",
|
"label.participants": "Teilnehmer",
|
||||||
"label.shareFile": "Teile Datai",
|
"label.shareFile": "Datei hochladen",
|
||||||
|
"label.shareGalleryFile": "Bild teilen",
|
||||||
"label.fileSharingUnsupported": "Dateifreigabe nicht unterstützt",
|
"label.fileSharingUnsupported": "Dateifreigabe nicht unterstützt",
|
||||||
"label.unknown": "Unbekannt",
|
"label.unknown": "Unbekannt",
|
||||||
"label.democratic": "Demokratisch",
|
"label.democratic": "Demokratisch",
|
||||||
"label.filmstrip": "Filmstreifen",
|
"label.filmstrip": "Filmstreifen",
|
||||||
"label.low": "Niedrig",
|
"label.low": "Niedrig",
|
||||||
"label.medium": "Medium",
|
"label.medium": "Mittel",
|
||||||
"label.high": "Hoch (HD)",
|
"label.high": "Hoch (HD)",
|
||||||
"label.veryHigh": "Sehr hoch (FHD)",
|
"label.veryHigh": "Sehr hoch (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Schließen",
|
"label.close": "Schließen",
|
||||||
|
"label.media": "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.settings": "Einstellungen",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
"settings.selectCamera": "Wähle Videogerät",
|
"settings.selectCamera": "Wähle ein Videogerät",
|
||||||
"settings.cantSelectCamera": "Kann Videogerät nicht aktivieren",
|
"settings.cantSelectCamera": "Kann Videogerät nicht aktivieren",
|
||||||
"settings.audio": "Audiogerät",
|
"settings.audio": "Audiogerät",
|
||||||
"settings.selectAudio": "Wähle Audiogerät",
|
"settings.selectAudio": "Wähle ein Audiogerät",
|
||||||
"settings.cantSelectAudio": "Kann Audiogerät nicht aktivieren",
|
"settings.cantSelectAudio": "Kann Audiogerät nicht aktivieren",
|
||||||
"settings.resolution": "Wähle Auflösung",
|
"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.layout": "Raumlayout",
|
"settings.layout": "Raumlayout",
|
||||||
"settings.selectRoomLayout": "Wähle Raumlayout",
|
"settings.selectRoomLayout": "Wähle ein Raumlayout",
|
||||||
"settings.advancedMode": "Erweiterter Modus",
|
"settings.advancedMode": "Erweiterter Modus",
|
||||||
"settings.permanentTopBar": "Permanente obere Leiste",
|
"settings.permanentTopBar": "Permanente obere Leiste",
|
||||||
"settings.lastn": "Anzahl der sichtbaren Videos",
|
"settings.lastn": "Anzahl der sichtbaren Videos",
|
||||||
|
"settings.hiddenControls": "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.saveFileError": "Fehler beim Speichern der Datei",
|
||||||
"filesharing.startingFileShare": "Starte Teilen der Datei",
|
"filesharing.startingFileShare": "Starte Teilen der Datei",
|
||||||
"filesharing.successfulFileShare": "Datei wurde geteilt",
|
"filesharing.successfulFileShare": "Datei wurde geteilt",
|
||||||
"filesharing.unableToShare": "Kann Datei nicht teilen",
|
"filesharing.unableToShare": "Datei kann nicht geteilt werden",
|
||||||
"filesharing.error": "Fehler beim Teilen der Datei",
|
"filesharing.error": "Fehler beim Teilen der Datei",
|
||||||
"filesharing.finished": "Datei heruntergeladen",
|
"filesharing.finished": "Datei heruntergeladen",
|
||||||
"filesharing.save": "Speichern",
|
"filesharing.save": "Speichern",
|
||||||
"filesharing.sharedFile": "{displayName} hat eine Datei geteilt",
|
"filesharing.sharedFile": "{displayName} hat eine Datei geteilt",
|
||||||
"filesharing.download": "Herunterladen",
|
"filesharing.download": "Herunterladen",
|
||||||
"filesharing.missingSeeds": "Wenn das Herunterladen nicht pausiert, ist wahrscheinlich niemand mehr im Raum, der die Datei teilen kann. Datei muss erneut geteilt werden.",
|
"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.",
|
||||||
|
|
||||||
"devices.devicesChanged": "Mediengeräte wurden aktualisiert und sind in Einstellungen verfügbar",
|
"devices.devicesChanged": "Mediengeräte wurden aktualisiert und sind in den Einstellungen verfügbar",
|
||||||
|
|
||||||
"device.audioUnsupported": "Audio nicht unterstützt",
|
"device.audioUnsupported": "Audio nicht unterstützt",
|
||||||
"device.activateAudio": "Aktiviere Audio",
|
"device.activateAudio": "Aktiviere Audio",
|
||||||
"device.muteAudio": "stummschalten",
|
"device.muteAudio": "Stummschalten",
|
||||||
"device.unMuteAudio": "Aktiviere Audio",
|
"device.unMuteAudio": "Stummschaltung aufheben",
|
||||||
|
|
||||||
"device.videoUnsupported": "Video nicht unterstützt",
|
"device.videoUnsupported": "Video nicht unterstützt",
|
||||||
"device.startVideo": "Starte Video",
|
"device.startVideo": "Starte Video",
|
||||||
"device.stopVideo": "Stoppe Video",
|
"device.stopVideo": "Stoppe Video",
|
||||||
|
|
||||||
"device.screenSharingUnsupported": "Bildschirmteilen nicht unterstützt",
|
"device.screenSharingUnsupported": "Bildschirmfreigabe nicht unterstützt",
|
||||||
"device.startScreenSharing": "Bildschirmteilen",
|
"device.startScreenSharing": "Starte Bildschirmfreigabe",
|
||||||
"device.stopScreenSharing": "Beende Bildschirmteilen",
|
"device.stopScreenSharing": "Beende Bildschirmfreigabe",
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Mikrophon nicht verbunden",
|
"devices.microphoneDisconnected": "Mikrofon nicht verbunden",
|
||||||
"devices.microphoneError": "Fehler mit Mikrophon",
|
"devices.microphoneError": "Fehler beim Zugriff auf dein Mikrofon",
|
||||||
"devices.microPhoneMute": "Mikrophon stumm geschaltet",
|
"devices.microphoneMute": "Mikrofon stummgeschaltet",
|
||||||
"devices.micophoneUnMute": "Mikrophon aktiviert",
|
"devices.microphoneUnMute": "Mikrofon aktiviert",
|
||||||
"devices.microphoneEnable": "Mikrofonen aktiviert",
|
"devices.microphoneEnable": "Mikrofon aktiviert",
|
||||||
"devices.microphoneMuteError": "Kann Mikrophon nicht stummschalten",
|
"devices.microphoneMuteError": "Kann Mikrofon nicht stummschalten",
|
||||||
"devices.microphoneUnMuteError": "Kann Mikrophon nicht aktivieren",
|
"devices.microphoneUnMuteError": "Kann Mikrofon nicht aktivieren",
|
||||||
|
|
||||||
"devices.screenSharingDisconnected" : "Bildschirmteilen unterbrochen",
|
"devices.screenSharingDisconnected" : "Bildschirmfreigabe unterbrochen",
|
||||||
"devices.screenSharingError": "Fehler beim Bildschirmteilen",
|
"devices.screenSharingError": "Fehler bei der Bildschirmfreigabe",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Video unterbrochen",
|
"devices.cameraDisconnected": "Kamera getrennt",
|
||||||
"devices.cameraError": "Fehler mit Videogerät"
|
"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:"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Deltagere i fokus",
|
"room.spotlights": "Deltagere i fokus",
|
||||||
"room.passive": "Passive deltagere",
|
"room.passive": "Passive deltagere",
|
||||||
"room.videoPaused": "Denne video er sat på pause",
|
"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.login": "Log ind",
|
||||||
"tooltip.logout": "Log ud",
|
"tooltip.logout": "Log ud",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Vis lobby",
|
"tooltip.lobby": "Vis lobby",
|
||||||
"tooltip.settings": "Vis indstillinger",
|
"tooltip.settings": "Vis indstillinger",
|
||||||
"tooltip.participants": "Vis deltagere",
|
"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.roomName": "Værelsesnavn",
|
||||||
"label.chooseRoomButton": "Fortsæt",
|
"label.chooseRoomButton": "Fortsæt",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Fildeling",
|
"label.filesharing": "Fildeling",
|
||||||
"label.participants": "Deltagere",
|
"label.participants": "Deltagere",
|
||||||
"label.shareFile": "Del fil",
|
"label.shareFile": "Del fil",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "Fildeling er ikke understøttet",
|
"label.fileSharingUnsupported": "Fildeling er ikke understøttet",
|
||||||
"label.unknown": "Ukendt",
|
"label.unknown": "Ukendt",
|
||||||
"label.democracy": "Galleri visning",
|
"label.democracy": "Galleri visning",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Meget høj (FHD)",
|
"label.veryHigh": "Meget høj (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Luk",
|
"label.close": "Luk",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearance": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
"label.promoteAllPeers": null,
|
||||||
|
"label.moreActions": null,
|
||||||
|
"label.version": null,
|
||||||
|
|
||||||
"settings.settings": "Indstillinger",
|
"settings.settings": "Indstillinger",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Lydenhed",
|
"settings.audio": "Lydenhed",
|
||||||
"settings.selectAudio": "Vælg lydenhed",
|
"settings.selectAudio": "Vælg lydenhed",
|
||||||
"settings.cantSelectAudio": "Kan ikke vælge 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.resolution": "Vælg din videoopløsning",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Møde visning",
|
"settings.layout": "Møde visning",
|
||||||
"settings.selectRoomLayout": "Vælg møde visning",
|
"settings.selectRoomLayout": "Vælg møde visning",
|
||||||
"settings.advancedMode": "Avanceret tilstand",
|
"settings.advancedMode": "Avanceret tilstand",
|
||||||
"settings.permanentTopBar": "Permanent øverste linje",
|
"settings.permanentTopBar": "Permanent øverste linje",
|
||||||
"settings.lastn": "Antal synlige videoer",
|
"settings.lastn": "Antal synlige videoer",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
"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.saveFileError": "Kan ikke gemme fil",
|
||||||
"filesharing.startingFileShare": "Forsøger at dele filen",
|
"filesharing.startingFileShare": "Forsøger at dele filen",
|
||||||
|
|
@ -106,7 +161,7 @@
|
||||||
"filesharing.finished": "Filen er færdig med at downloade",
|
"filesharing.finished": "Filen er færdig med at downloade",
|
||||||
"filesharing.save": "Gem",
|
"filesharing.save": "Gem",
|
||||||
"filesharing.sharedFile": "{displayName} delte en fil",
|
"filesharing.sharedFile": "{displayName} delte en fil",
|
||||||
"filesharing.download": "Download",
|
"filesharing.download": null,
|
||||||
"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.",
|
"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",
|
"device.devicesChanged": "Detekteret ndringer i dine enheder, konfigurer dine enheder i indstillingsdialogen",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"device.microphoneDisconnected": "Mikrofon frakoblet",
|
"device.microphoneDisconnected": "Mikrofon frakoblet",
|
||||||
"device.microphoneError": "Der opstod en fejl under adgang til din mikrofon",
|
"device.microphoneError": "Der opstod en fejl under adgang til din mikrofon",
|
||||||
"device.microPhoneMute": "Dæmp din mikrofon",
|
"device.microphoneMute": "Dæmp din mikrofon",
|
||||||
"device.micophoneUnMute": "Slå ikke lyden fra din mikrofon",
|
"device.microphoneUnMute": "Slå ikke lyden fra din mikrofon",
|
||||||
"device.microphoneEnable": "Aktiveret din mikrofon",
|
"device.microphoneEnable": "Aktiveret din mikrofon",
|
||||||
"device.microphoneMuteError": "Kan ikke slå din mikrofon fra",
|
"device.microphoneMuteError": "Kan ikke slå din mikrofon fra",
|
||||||
"device.microphoneUnMuteError": "Kan ikke slå lyden til på din mikrofon",
|
"device.microphoneUnMuteError": "Kan ikke slå lyden til på din mikrofon",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Der opstod en fejl ved adgang til skærmdeling",
|
"devices.screenSharingError": "Der opstod en fejl ved adgang til skærmdeling",
|
||||||
|
|
||||||
"device.cameraDisconnected": "Kamera frakoblet",
|
"device.cameraDisconnected": "Kamera frakoblet",
|
||||||
"device.cameraError": "Der opstod en fejl ved tilkobling af dit kamera"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Συμμετέχοντες στο Spotlight",
|
"room.spotlights": "Συμμετέχοντες στο Spotlight",
|
||||||
"room.passive": "Παθητικοί συμμετέχοντες",
|
"room.passive": "Παθητικοί συμμετέχοντες",
|
||||||
"room.videoPaused": "Το βίντεο έχει σταματήσει",
|
"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.login": "Σύνδεση",
|
||||||
"tooltip.logout": "Αποσύνδεση",
|
"tooltip.logout": "Αποσύνδεση",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Εμφάνιση λόμπι",
|
"tooltip.lobby": "Εμφάνιση λόμπι",
|
||||||
"tooltip.settings": "Εμφάνιση ρυθμίσεων",
|
"tooltip.settings": "Εμφάνιση ρυθμίσεων",
|
||||||
"tooltip.participants": "Εμφάνιση συμμετεχόντων",
|
"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.roomName": "Όνομα δωματίου",
|
||||||
"label.chooseRoomButton": "Συνέχεια",
|
"label.chooseRoomButton": "Συνέχεια",
|
||||||
|
|
@ -73,16 +102,24 @@
|
||||||
"label.filesharing": "Διαμοιρασμοός αρχείου",
|
"label.filesharing": "Διαμοιρασμοός αρχείου",
|
||||||
"label.participants": "Συμμετέχοντες",
|
"label.participants": "Συμμετέχοντες",
|
||||||
"label.shareFile": "Διαμοιραστείτε ένα αρχείο",
|
"label.shareFile": "Διαμοιραστείτε ένα αρχείο",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "Ο διαμοιρασμός αρχείων δεν υποστηρίζεται",
|
"label.fileSharingUnsupported": "Ο διαμοιρασμός αρχείων δεν υποστηρίζεται",
|
||||||
"label.unknown": "Άγνωστο",
|
"label.unknown": "Άγνωστο",
|
||||||
"label.democratic": "Democratic view",
|
"label.democratic": null,
|
||||||
"label.filmstrip": "Filmstrip view",
|
"label.filmstrip": null,
|
||||||
"label.low": "Χαμηλή",
|
"label.low": "Χαμηλή",
|
||||||
"label.medium": "Μέτρια",
|
"label.medium": "Μέτρια",
|
||||||
"label.high": "Υψηλή (HD)",
|
"label.high": "Υψηλή (HD)",
|
||||||
"label.veryHigh": "Πολύ υψηλή (FHD)",
|
"label.veryHigh": "Πολύ υψηλή (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Κλείσιμο",
|
"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.settings": "Ρυθμίσεις",
|
||||||
"settings.camera": "Κάμερα",
|
"settings.camera": "Κάμερα",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Συσκευή ήχου",
|
"settings.audio": "Συσκευή ήχου",
|
||||||
"settings.selectAudio": "Επιλογή συσκευής ήχου",
|
"settings.selectAudio": "Επιλογή συσκευής ήχου",
|
||||||
"settings.cantSelectAudio": "Αδυναμία επιλογής συσκευής ήχου",
|
"settings.cantSelectAudio": "Αδυναμία επιλογής συσκευής ήχου",
|
||||||
|
"settings.audioOutput": "Συσκευή εξόδου ήχου",
|
||||||
|
"settings.selectAudioOutput": "Επιλέξτε συσκευή εξόδου ήχου",
|
||||||
|
"settings.cantSelectAudioOutput": "Δεν είναι δυνατή η επιλογή συσκευής εξόδου ήχου",
|
||||||
"settings.resolution": "Επιλέξτε την ανάλυση του video",
|
"settings.resolution": "Επιλέξτε την ανάλυση του video",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Περιβάλλον δωματίου",
|
"settings.layout": "Περιβάλλον δωματίου",
|
||||||
"settings.selectRoomLayout": "Επιλογή περιβάλλοντος δωματίου",
|
"settings.selectRoomLayout": "Επιλογή περιβάλλοντος δωματίου",
|
||||||
"settings.advancedMode": "Προηγμένη λειτουργία",
|
"settings.advancedMode": "Προηγμένη λειτουργία",
|
||||||
"settings.permanentTopBar": "Μόνιμη μπάρα κορυφής",
|
"settings.permanentTopBar": "Μόνιμη μπάρα κορυφής",
|
||||||
"settings.lastn": "Αριθμός ορατών βίντεο",
|
"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.saveFileError": "Αδυναμία αποθήκευσης του αρχείου",
|
||||||
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
|
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Το μικρόφωνο αποσυνδέθηκε",
|
"devices.microphoneDisconnected": "Το μικρόφωνο αποσυνδέθηκε",
|
||||||
"devices.microphoneError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στο μικρόφωνό σας",
|
"devices.microphoneError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στο μικρόφωνό σας",
|
||||||
"devices.microPhoneMute": "Το μικρόφωνό σας είναι σε σίγαση",
|
"devices.microphoneMute": "Το μικρόφωνό σας είναι σε σίγαση",
|
||||||
"devices.micophoneUnMute": "Ανοίξτε το μικρόφωνό σας",
|
"devices.microphoneUnMute": "Ανοίξτε το μικρόφωνό σας",
|
||||||
"devices.microphoneEnable": "Ενεργοποίησε το μικρόφωνό σας",
|
"devices.microphoneEnable": "Ενεργοποίησε το μικρόφωνό σας",
|
||||||
"devices.microphoneMuteError": "Δεν είναι δυνατή η σίγαση του μικροφώνου σας",
|
"devices.microphoneMuteError": "Δεν είναι δυνατή η σίγαση του μικροφώνου σας",
|
||||||
"devices.microphoneUnMuteError": "Δεν είναι δυνατό το άνοιγμα του μικροφώνου σας",
|
"devices.microphoneUnMuteError": "Δεν είναι δυνατό το άνοιγμα του μικροφώνου σας",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στην οθόνη σας",
|
"devices.screenSharingError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στην οθόνη σας",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Η κάμερα αποσυνδέθηκε",
|
"devices.cameraDisconnected": "Η κάμερα αποσυνδέθηκε",
|
||||||
"devices.cameraError": "Παρουσιάστηκε σφάλμα κατά την πρόσβαση στην κάμερά σας"
|
"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
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Participants in Spotlight",
|
"room.spotlights": "Participants in Spotlight",
|
||||||
"room.passive": "Passive Participants",
|
"room.passive": "Passive Participants",
|
||||||
"room.videoPaused": "This video is paused",
|
"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.login": "Log in",
|
||||||
"tooltip.logout": "Log out",
|
"tooltip.logout": "Log out",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Show lobby",
|
"tooltip.lobby": "Show lobby",
|
||||||
"tooltip.settings": "Show settings",
|
"tooltip.settings": "Show settings",
|
||||||
"tooltip.participants": "Show participants",
|
"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.roomName": "Room name",
|
||||||
"label.chooseRoomButton": "Continue",
|
"label.chooseRoomButton": "Continue",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "File sharing",
|
"label.filesharing": "File sharing",
|
||||||
"label.participants": "Participants",
|
"label.participants": "Participants",
|
||||||
"label.shareFile": "Share file",
|
"label.shareFile": "Share file",
|
||||||
|
"label.shareGalleryFile": "Share image",
|
||||||
"label.fileSharingUnsupported": "File sharing not supported",
|
"label.fileSharingUnsupported": "File sharing not supported",
|
||||||
"label.unknown": "Unknown",
|
"label.unknown": "Unknown",
|
||||||
"label.democratic": "Democratic view",
|
"label.democratic": "Democratic view",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Very high (FHD)",
|
"label.veryHigh": "Very high (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Close",
|
"label.close": "Close",
|
||||||
|
"label.media": "Media",
|
||||||
|
"label.appearance": "Appearence",
|
||||||
|
"label.advanced": "Advanced",
|
||||||
|
"label.addVideo": "Add video",
|
||||||
|
"label.promoteAllPeers": "Promote all",
|
||||||
|
"label.moreActions": "More actions",
|
||||||
|
"label.version": "Version",
|
||||||
|
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"settings.camera": "Camera",
|
"settings.camera": "Camera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Audio device",
|
"settings.audio": "Audio device",
|
||||||
"settings.selectAudio": "Select audio device",
|
"settings.selectAudio": "Select audio device",
|
||||||
"settings.cantSelectAudio": "Unable to 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.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.layout": "Room layout",
|
||||||
"settings.selectRoomLayout": "Select room layout",
|
"settings.selectRoomLayout": "Select room layout",
|
||||||
"settings.advancedMode": "Advanced mode",
|
"settings.advancedMode": "Advanced mode",
|
||||||
"settings.permanentTopBar": "Permanent top bar",
|
"settings.permanentTopBar": "Permanent top bar",
|
||||||
"settings.lastn": "Number of visible videos",
|
"settings.lastn": "Number of visible videos",
|
||||||
|
"settings.hiddenControls": "Hidden media controls",
|
||||||
|
"settings.notificationSounds": "Notification sounds",
|
||||||
|
"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.saveFileError": "Unable to save file",
|
||||||
"filesharing.startingFileShare": "Attempting to share file",
|
"filesharing.startingFileShare": "Attempting to share file",
|
||||||
|
|
@ -125,9 +180,9 @@
|
||||||
"device.stopScreenSharing": "Stop screen sharing",
|
"device.stopScreenSharing": "Stop screen sharing",
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Microphone disconnected",
|
"devices.microphoneDisconnected": "Microphone disconnected",
|
||||||
"devices.microphoneError": "An error occurred while accessing your microphone",
|
"devices.microphoneError": "An error occured while accessing your microphone",
|
||||||
"devices.microPhoneMute": "Muted your microphone",
|
"devices.microphoneMute": "Muted your microphone",
|
||||||
"devices.micophoneUnMute": "Unmuted your microphone",
|
"devices.microphoneUnMute": "Unmuted your microphone",
|
||||||
"devices.microphoneEnable": "Enabled your microphone",
|
"devices.microphoneEnable": "Enabled your microphone",
|
||||||
"devices.microphoneMuteError": "Unable to mute your microphone",
|
"devices.microphoneMuteError": "Unable to mute your microphone",
|
||||||
"devices.microphoneUnMuteError": "Unable to unmute your microphone",
|
"devices.microphoneUnMuteError": "Unable to unmute your microphone",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "An error occurred while accessing your screen",
|
"devices.screenSharingError": "An error occurred while accessing your screen",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Camera disconnected",
|
"devices.cameraDisconnected": "Camera disconnected",
|
||||||
"devices.cameraError": "An error occurred while accessing your camera"
|
"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:"
|
||||||
}
|
}
|
||||||
|
|
@ -1,140 +1,205 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected": "Desconectado",
|
"socket.disconnected": "Desconectado",
|
||||||
"socket.reconnecting": "Desconectado, intentando reconectar",
|
"socket.reconnecting": "Desconectado, intentando reconectar",
|
||||||
"socket.reconnected": "Reconectado",
|
"socket.reconnected": "Reconectado",
|
||||||
"socket.requestError": "Error en la petición al servidor",
|
"socket.requestError": "Error en la petición al servidor",
|
||||||
|
|
||||||
"room.chooseRoom": "Indique el nombre de la sala a la que le gustaría unirse",
|
"room.chooseRoom": "Indique el nombre de la sala a la que le gustaría unirse",
|
||||||
"room.cookieConsent": "Esta web utiliza cookies para mejorar la experiencia de usuario",
|
"room.cookieConsent": "Esta web utiliza cookies para mejorar la experiencia de usuario",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "I understand",
|
||||||
"room.joined": "Se ha unido a la sala",
|
"room.joined": "Se ha unido a la sala",
|
||||||
"room.cantJoin": "No ha sido posible unirse a la sala",
|
"room.cantJoin": "No ha sido posible unirse a la sala",
|
||||||
"room.youLocked": "Ha cerrado la sala",
|
"room.youLocked": "Ha cerrado la sala",
|
||||||
"room.cantLock": "No ha sido posible cerrar la sala",
|
"room.cantLock": "No ha sido posible cerrar la sala",
|
||||||
"room.youUnLocked": "Ha abierto la sala",
|
"room.youUnLocked": "Ha abierto la sala",
|
||||||
"room.cantUnLock": "No ha sido posible abrir la sala",
|
"room.cantUnLock": "No ha sido posible abrir la sala",
|
||||||
"room.locked": "La sala ahora es privada",
|
"room.locked": "La sala ahora es privada",
|
||||||
"room.unlocked": "La sala ahora es pública",
|
"room.unlocked": "La sala ahora es pública",
|
||||||
"room.newLobbyPeer": "Nuevo participante en la sala de espera",
|
"room.newLobbyPeer": "Nuevo participante en la sala de espera",
|
||||||
"room.lobbyPeerLeft": "Un participante en espera ha salido",
|
"room.lobbyPeerLeft": "Un participante en espera ha salido",
|
||||||
"room.lobbyPeerChangedDisplayName": "Participante en espera cambió su nombre a {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Participante en espera cambió su nombre a {displayName}",
|
||||||
"room.lobbyPeerChangedPicture": "Participante en espera cambió su foto",
|
"room.lobbyPeerChangedPicture": "Participante en espera cambió su foto",
|
||||||
"room.setAccessCode": "Código de acceso de la sala actualizado",
|
"room.setAccessCode": "Código de acceso de la sala actualizado",
|
||||||
"room.accessCodeOn": "Código de acceso de la sala activado",
|
"room.accessCodeOn": "Código de acceso de la sala activado",
|
||||||
"room.accessCodeOff": "Código de acceso de la sala desactivado",
|
"room.accessCodeOff": "Código de acceso de la sala desactivado",
|
||||||
"room.peerChangedDisplayName": "{oldDisplayName} es ahora {displayName}",
|
"room.peerChangedDisplayName": "{oldDisplayName} es ahora {displayName}",
|
||||||
"room.newPeer": "{displayName} se unió a la sala",
|
"room.newPeer": "{displayName} se unió a la sala",
|
||||||
"room.newFile": "Nuevo fichero disponible",
|
"room.newFile": "Nuevo fichero disponible",
|
||||||
"room.toggleAdvancedMode": "Cambiado a modo avanzado",
|
"room.toggleAdvancedMode": "Cambiado a modo avanzado",
|
||||||
"room.setDemocraticView": "Cambiado a modo democrático",
|
"room.setDemocraticView": "Cambiado a modo democrático",
|
||||||
"room.setFilmStripView": "Cambiado a modo viñeta",
|
"room.setFilmStripView": "Cambiado a modo viñeta",
|
||||||
"room.loggedIn": "Ha iniciado sesión",
|
"room.loggedIn": "Ha iniciado sesión",
|
||||||
"room.loggedOut": "Ha cerrado su sesión",
|
"room.loggedOut": "Ha cerrado su sesión",
|
||||||
"room.changedDisplayName": "Ha cambiado su nombre a {displayName}",
|
"room.changedDisplayName": "Ha cambiado su nombre a {displayName}",
|
||||||
"room.changeDisplayNameError": "Hubo un error al intentar cambiar su nombre",
|
"room.changeDisplayNameError": "Hubo un error al intentar cambiar su nombre",
|
||||||
"room.chatError": "No ha sido posible enviar su mensaje",
|
"room.chatError": "No ha sido posible enviar su mensaje",
|
||||||
"room.aboutToJoin": "Está a punto de unirse a una reunión",
|
"room.aboutToJoin": "Está a punto de unirse a una reunión",
|
||||||
"room.roomId": "ID de la sala: {roomName}",
|
"room.roomId": "ID de la sala: {roomName}",
|
||||||
"room.setYourName": "Indique el nombre con el que quiere participar y cómo quiere unirse:",
|
"room.setYourName": "Indique el nombre con el que quiere participar y cómo quiere unirse:",
|
||||||
"room.audioOnly": "Solo sonido",
|
"room.audioOnly": "Solo sonido",
|
||||||
"room.audioVideo": "Sonido y vídeo",
|
"room.audioVideo": "Sonido y vídeo",
|
||||||
"room.youAreReady": "Ok, está preparado",
|
"room.youAreReady": "Ok, está preparado",
|
||||||
"room.emptyRequireLogin": "¡La sala está vacía! Puede iniciar sesión para comenzar la reunión o esperar hasta que el anfitrión se una",
|
"room.emptyRequireLogin": "¡La sala está vacía! Puede iniciar sesión para comenzar la reunión o esperar hasta que el anfitrión se una",
|
||||||
"room.locketWait": "La sala es privada - espere hasta que alguien le invite ...",
|
"room.locketWait": "La sala es privada - espere hasta que alguien le invite ...",
|
||||||
"room.lobbyAdministration": "Administración de la sala de espera",
|
"room.lobbyAdministration": "Administración de la sala de espera",
|
||||||
"room.peersInLobby": "Participantes en la sala de espera",
|
"room.peersInLobby": "Participantes en la sala de espera",
|
||||||
"room.lobbyEmpty": "La sala de espera está vacía",
|
"room.lobbyEmpty": "La sala de espera está vacía",
|
||||||
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participante} other {participantes}}",
|
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participante} other {participantes}}",
|
||||||
"room.me": "Yo",
|
"room.me": "Yo",
|
||||||
"room.spotlights": "Participantes destacados",
|
"room.spotlights": "Participantes destacados",
|
||||||
"room.passive": "Participantes pasivos",
|
"room.passive": "Participantes pasivos",
|
||||||
"room.videoPaused": "El vídeo está pausado",
|
"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,
|
||||||
|
|
||||||
"tooltip.login": "Entrar",
|
"me.mutedPTT": null,
|
||||||
"tooltip.logout": "Salir",
|
|
||||||
"tooltip.admitFromLobby": "Admitir desde la sala de espera",
|
|
||||||
"tooltip.lockRoom": "Configurar sala como privada",
|
|
||||||
"tooltip.unLockRoom": "Configurar sala como pública",
|
|
||||||
"tooltip.enterFullscreen": "Presentar en pantalla completa",
|
|
||||||
"tooltip.leaveFullscreen": "Salir de la pantalla completa",
|
|
||||||
"tooltip.lobby": "Mostrar sala de espera",
|
|
||||||
"tooltip.settings": "Mostrar ajustes",
|
|
||||||
"tooltip.participants": "Mostrar participantes",
|
|
||||||
|
|
||||||
"label.roomName": "Nombre de la sala",
|
"roles.gotRole": null,
|
||||||
"label.chooseRoomButton": "Continuar",
|
"roles.lostRole": null,
|
||||||
"label.yourName": "Su nombre",
|
|
||||||
"label.newWindow": "Nueva ventana",
|
|
||||||
"label.fullscreen": "Pantalla completa",
|
|
||||||
"label.openDrawer": "Abrir panel",
|
|
||||||
"label.leave": "Salir",
|
|
||||||
"label.chatInput": "Escriba su mensaje...",
|
|
||||||
"label.chat": "Chat",
|
|
||||||
"label.filesharing": "Compartir ficheros",
|
|
||||||
"label.participants": "Participantes",
|
|
||||||
"label.shareFile": "Compartir fichero",
|
|
||||||
"label.fileSharingUnsupported": "Compartir ficheros no está disponible",
|
|
||||||
"label.unknown": "Desconocido",
|
|
||||||
"label.democratic": "Vista democrática",
|
|
||||||
"label.filmstrip": "Vista en viñeta",
|
|
||||||
"label.low": "Baja",
|
|
||||||
"label.medium": "Media",
|
|
||||||
"label.high": "Alta (HD)",
|
|
||||||
"label.veryHigh": "Muy alta (FHD)",
|
|
||||||
"label.ultra": "Ultra (UHD)",
|
|
||||||
"label.close": "Cerrar",
|
|
||||||
|
|
||||||
"settings.settings": "Ajustes",
|
"tooltip.login": "Entrar",
|
||||||
"settings.camera": "Cámara",
|
"tooltip.logout": "Salir",
|
||||||
"settings.selectCamera": "Seleccionar dispositivo de vídeo",
|
"tooltip.admitFromLobby": "Admitir desde la sala de espera",
|
||||||
"settings.cantSelectCamera": "No ha sido posible seleccionar el dispositivo de vídeo",
|
"tooltip.lockRoom": "Configurar sala como privada",
|
||||||
"settings.audio": "Dispositivo de sonido",
|
"tooltip.unLockRoom": "Configurar sala como pública",
|
||||||
"settings.selectAudio": "Seleccione dispositivo de sonido",
|
"tooltip.enterFullscreen": "Presentar en pantalla completa",
|
||||||
"settings.cantSelectAudio": "No ha sido posible seleccionar el dispositivo de sonido",
|
"tooltip.leaveFullscreen": "Salir de la pantalla completa",
|
||||||
"settings.resolution": "Seleccione su resolución de imagen",
|
"tooltip.lobby": "Mostrar sala de espera",
|
||||||
"settings.layout": "Disposición de la sala",
|
"tooltip.settings": "Mostrar ajustes",
|
||||||
"settings.selectRoomLayout": "Seleccione la disposición de la sala",
|
"tooltip.participants": "Mostrar participantes",
|
||||||
"settings.advancedMode": "Modo avanzado",
|
"tooltip.kickParticipant": null,
|
||||||
"settings.permanentTopBar": "Barra superior permanente",
|
"tooltip.muteParticipant": null,
|
||||||
"settings.lastn": "Cantidad de videos visibles",
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
"tooltip.muteScreenSharing": null,
|
||||||
|
"tooltip.muteParticipantAudioModerator": null,
|
||||||
|
"tooltip.muteParticipantVideoModerator": null,
|
||||||
|
"tooltip.muteScreenSharingModerator": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
"label.roomName": "Nombre de la sala",
|
||||||
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
"label.chooseRoomButton": "Continuar",
|
||||||
"filesharing.successfulFileShare": "El fichero se compartió con éxito",
|
"label.yourName": "Su nombre",
|
||||||
"filesharing.unableToShare": "No ha sido posible compartir el fichero",
|
"label.newWindow": "Nueva ventana",
|
||||||
"filesharing.error": "Hubo un error al compartir el fichero",
|
"label.fullscreen": "Pantalla completa",
|
||||||
"filesharing.finished": "Descarga del fichero finalizada",
|
"label.openDrawer": "Abrir panel",
|
||||||
"filesharing.save": "Guardar",
|
"label.leave": "Salir",
|
||||||
"filesharing.sharedFile": "{displayName} compartió un fichero",
|
"label.chatInput": "Escriba su mensaje...",
|
||||||
"filesharing.download": "Descargar",
|
"label.chat": "Chat",
|
||||||
"filesharing.missingSeeds": "Si este proceso demora en exceso, puede ocurrir que no haya nadie compartiendo el fichero. Pruebe a pedirle a alguien que vuelva a subir el fichero que busca.",
|
"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",
|
||||||
|
"label.filmstrip": "Vista en viñeta",
|
||||||
|
"label.low": "Baja",
|
||||||
|
"label.medium": "Media",
|
||||||
|
"label.high": "Alta (HD)",
|
||||||
|
"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,
|
||||||
|
|
||||||
"devices.devicesChanged": "Sus dispositivos han cambiado, vuelva a configurarlos en la ventana de ajustes",
|
"settings.settings": "Ajustes",
|
||||||
|
"settings.camera": "Cámara",
|
||||||
|
"settings.selectCamera": "Seleccionar dispositivo de vídeo",
|
||||||
|
"settings.cantSelectCamera": "No ha sido posible seleccionar el dispositivo de vídeo",
|
||||||
|
"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,
|
||||||
|
|
||||||
"device.audioUnsupported": "Sonido no disponible",
|
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
||||||
"device.activateAudio": "Activar sonido",
|
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
||||||
"device.muteAudio": "Silenciar sonido",
|
"filesharing.successfulFileShare": "El fichero se compartió con éxito",
|
||||||
"device.unMuteAudio": "Reactivar sonido",
|
"filesharing.unableToShare": "No ha sido posible compartir el fichero",
|
||||||
|
"filesharing.error": "Hubo un error al compartir el fichero",
|
||||||
|
"filesharing.finished": "Descarga del fichero finalizada",
|
||||||
|
"filesharing.save": "Guardar",
|
||||||
|
"filesharing.sharedFile": "{displayName} compartió un fichero",
|
||||||
|
"filesharing.download": "Descargar",
|
||||||
|
"filesharing.missingSeeds": "Si este proceso demora en exceso, puede ocurrir que no haya nadie compartiendo el fichero. Pruebe a pedirle a alguien que vuelva a subir el fichero que busca.",
|
||||||
|
|
||||||
"device.videoUnsupported": "Vídeo no disponible",
|
"devices.devicesChanged": "Sus dispositivos han cambiado, vuelva a configurarlos en la ventana de ajustes",
|
||||||
"device.startVideo": "Iniciar vídeo",
|
|
||||||
"device.stopVideo": "Detener vídeo",
|
|
||||||
|
|
||||||
"device.screenSharingUnsupported": "Compartir pantalla no disponible",
|
"device.audioUnsupported": "Sonido no disponible",
|
||||||
"device.startScreenSharing": "Iniciar compartir pantalla",
|
"device.activateAudio": "Activar sonido",
|
||||||
"device.stopScreenSharing": "Detener compartir pantalla",
|
"device.muteAudio": "Silenciar sonido",
|
||||||
|
"device.unMuteAudio": "Reactivar sonido",
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Micrófono desconectado",
|
"device.videoUnsupported": "Vídeo no disponible",
|
||||||
"devices.microphoneError": "Hubo un error al acceder a su micrófono",
|
"device.startVideo": "Iniciar vídeo",
|
||||||
"devices.microPhoneMute": "Desactivar micrófono",
|
"device.stopVideo": "Detener vídeo",
|
||||||
"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",
|
|
||||||
|
|
||||||
"devices.screenSharingDisconnected": "Pantalla compartida desconectada",
|
"device.screenSharingUnsupported": "Compartir pantalla no disponible",
|
||||||
"devices.screenSharingError": "Hubo un error al acceder a su pantalla",
|
"device.startScreenSharing": "Iniciar compartir pantalla",
|
||||||
|
"device.stopScreenSharing": "Detener compartir pantalla",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Cámara desconectada",
|
"devices.microphoneDisconnected": "Micrófono desconectado",
|
||||||
"devices.cameraError": "Hubo un error al acceder a su cámara"
|
"devices.microphoneError": "Hubo un error al acceder a su micrófono",
|
||||||
|
"devices.microphoneMute": "Desactivar micrófono",
|
||||||
|
"devices.microphoneUnMute": "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",
|
||||||
|
|
||||||
|
"devices.screenSharingDisconnected": "Pantalla compartida desconectada",
|
||||||
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,139 +1,204 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected" : " Vous avez été déconnecté",
|
"socket.disconnected": "Vous avez été déconnecté",
|
||||||
"socket.reconnecting" : " Vous avez été déconnecté, reconnexion en cours",
|
"socket.reconnecting": "Vous avez été déconnecté, reconnexion en cours",
|
||||||
"socket.reconnected" : " Vous êtes reconnecté",
|
"socket.reconnected": "Vous êtes reconnecté",
|
||||||
"socket.requestError" : " Erreur sur une requête serveur",
|
"socket.requestError": "Erreur sur une requête serveur",
|
||||||
|
|
||||||
"room.chooseRoom" : " Choisissez le nom de la réunion que vous souhaitez rejoindre",
|
"room.chooseRoom": "Choisissez le nom de la réunion que vous souhaitez rejoindre",
|
||||||
"room.cookieConsent" : " Ce site utilise les cookies pour améliorer votre expérience utilisateur",
|
"room.cookieConsent": "Ce site utilise les cookies pour améliorer votre expérience utilisateur",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "I understand",
|
||||||
"room.joined" : " Vous avez rejoint la salle",
|
"room.joined": "Vous avez rejoint la salle",
|
||||||
"room.cantJoin" : " Impossible de rejoindre la salle",
|
"room.cantJoin": "Impossible de rejoindre la salle",
|
||||||
"room.youLocked" : " Vous avez privatisé la salle",
|
"room.youLocked": "Vous avez privatisé la salle",
|
||||||
"room.cantLock" : " Impossible de privatiser la salle",
|
"room.cantLock": "Impossible de privatiser la salle",
|
||||||
"room.youUnLocked" : " Vous avez dé-privatiser la salle",
|
"room.youUnLocked": "Vous avez dé-privatisé la salle",
|
||||||
"room.cantUnLock" : " Impossible de dé-privatiser la réunion",
|
"room.cantUnLock": "Impossible de dé-privatiser la réunion",
|
||||||
"room.locked" : " La réunion est privée",
|
"room.locked": "La réunion est privée",
|
||||||
"room.unlocked" : " La réunion est publique",
|
"room.unlocked": "La réunion est publique",
|
||||||
"room.newLobbyPeer" : " Un nouveau participant est dans la salle d’attente",
|
"room.newLobbyPeer": "Un nouveau participant est dans la salle d’attente",
|
||||||
"room.lobbyPeerLeft" : " Un participant de la salle d’attente vient de partir",
|
"room.lobbyPeerLeft": "Un participant de la salle d’attente vient de partir",
|
||||||
"room.lobbyPeerChangedDisplayName" : " Un participant dans la salle d’attente a changé de nom pour {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Un participant dans la salle d’attente a changé de nom pour {displayName}",
|
||||||
"room.lobbyPeerChangedPicture" : " Un participant dans le hall à changer de photo",
|
"room.lobbyPeerChangedPicture": "Un participant dans le hall à changer de photo",
|
||||||
"room.setAccessCode" : " Code d’accès à la réunion mis à jour",
|
"room.setAccessCode": "Code d’accès à la réunion mis à jour",
|
||||||
"room.accessCodeOn" : " Code d’accès à la réunion activée",
|
"room.accessCodeOn": "Code d’accès à la réunion activé",
|
||||||
"room.accessCodeOff" : " Code d’accès à la réunion désactivée",
|
"room.accessCodeOff": "Code d’accès à la réunion désactivé",
|
||||||
"room.peerChangedDisplayName" : " {oldDisplayName} est maintenant {displayName}",
|
"room.peerChangedDisplayName": "{oldDisplayName} est maintenant {displayName}",
|
||||||
"room.newPeer" : " {displayName} a rejoint la salle",
|
"room.newPeer": "{displayName} a rejoint la salle",
|
||||||
"room.newFile" : " Nouveau fichier disponible",
|
"room.newFile": "Nouveau fichier disponible",
|
||||||
"room.toggleAdvancedMode" : " Basculer en mode avancé",
|
"room.toggleAdvancedMode": "Basculer en mode avancé",
|
||||||
"room.setDemocraticView" : " Passer en vue démocratique",
|
"room.setDemocraticView": "Passer en vue démocratique",
|
||||||
"room.setFilmStripView" : " Passer en vue vignette",
|
"room.setFilmStripView": "Passer en vue vignette",
|
||||||
"room.loggedIn" : " Vous êtes connecté",
|
"room.loggedIn": "Vous êtes connecté",
|
||||||
"room.loggedOut" : " Vous êtes déconnecté",
|
"room.loggedOut": "Vous êtes déconnecté",
|
||||||
"room.changedDisplayName" : " Votre nom à changer pour {displayname}",
|
"room.changedDisplayName": "Votre nom a changé pour {displayname}",
|
||||||
"room.changeDisplayNameError" : " Une erreur s’est produite pour votre changement de nom",
|
"room.changeDisplayNameError": "Une erreur s’est produite pour votre changement de nom",
|
||||||
"room.chatError" : " Impossible d’envoyer un message",
|
"room.chatError": "Impossible d’envoyer un message",
|
||||||
"room.aboutToJoin" : " Vous allez rejoindre une réunion",
|
"room.aboutToJoin": "Vous allez rejoindre une réunion",
|
||||||
"room.roomId" : " Salle ID: {roomName}",
|
"room.roomId": "Salle ID: {roomName}",
|
||||||
"room.setYourName" : " Choisissez votre nom de participant puis comment vous connecter :",
|
"room.setYourName": "Choisissez votre nom de participant puis comment vous connecter :",
|
||||||
"room.audioOnly" : " Audio uniquement",
|
"room.audioOnly": "Audio uniquement",
|
||||||
"room.audioVideo" : " Audio et Vidéo",
|
"room.audioVideo": "Audio et Vidéo",
|
||||||
"room.youAreReady" : " Ok, vous êtes prêt",
|
"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 quelqu’un vous laisse entrer",
|
"room.locketWait": "La réunion est privatisée - attendez que quelqu’un vous laisse entrer",
|
||||||
"room.lobbyAdministration" : " Administration de la salle d’attente",
|
"room.lobbyAdministration": "Administration de la salle d’attente",
|
||||||
"room.peersInLobby" : " Participants dans la salle d’attente",
|
"room.peersInLobby": "Participants dans la salle d’attente",
|
||||||
"room.lobbyEmpty" : " Il n'y a actuellement aucun participant dans la salle d'attente",
|
"room.lobbyEmpty": "Il n'y a actuellement aucun participant dans la salle d'attente",
|
||||||
"room.hiddenPeers" : " {hiddenPeersCount, plural, one {participant} other {participants}}",
|
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
|
||||||
"room.me" : " Moi",
|
"room.me": "Moi",
|
||||||
"room.spotlights" : " Participants actifs",
|
"room.spotlights": "Participants actifs",
|
||||||
"room.passive" : " Participants passifs",
|
"room.passive": "Participants passifs",
|
||||||
"room.videoPaused" : " La vidéo est en pause",
|
"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,
|
||||||
|
|
||||||
"tooltip.login" : " Connexion",
|
"me.mutedPTT": null,
|
||||||
"tooltip.logout" : " Déconnexion",
|
|
||||||
"tooltip.admitFromLobby" : " Autorisé depuis la salle d'attente",
|
"roles.gotRole": null,
|
||||||
"tooltip.lockRoom" : " Privatisation de la salle",
|
"roles.lostRole": null,
|
||||||
"tooltip.unLockRoom" : " Dé-privatisation de la salle",
|
|
||||||
"tooltip.enterFullscreen" : " Afficher en plein écran",
|
"tooltip.login": "Connexion",
|
||||||
"tooltip.leaveFullscreen" : " Quitter le plein écran",
|
"tooltip.logout": "Déconnexion",
|
||||||
"tooltip.lobby" : " Afficher la salle d'attente",
|
"tooltip.admitFromLobby": "Autorisé depuis la salle d'attente",
|
||||||
"tooltip.settings" : " Afficher les paramètres",
|
"tooltip.lockRoom": "Privatisation de la salle",
|
||||||
|
"tooltip.unLockRoom": "Dé-privatisation de la salle",
|
||||||
|
"tooltip.enterFullscreen": "Afficher en plein écran",
|
||||||
|
"tooltip.leaveFullscreen": "Quitter le plein écran",
|
||||||
|
"tooltip.lobby": "Afficher la salle d'attente",
|
||||||
|
"tooltip.settings": "Afficher les paramètres",
|
||||||
"tooltip.participants": "Afficher les participants",
|
"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.roomName": "Nom de la salle",
|
||||||
"label.chooseRoomButton" : " Continuer",
|
"label.chooseRoomButton": "Continuer",
|
||||||
"label.yourName" : " Votre nom",
|
"label.yourName": "Votre nom",
|
||||||
"label.newWindow" : " Nouvelle fenêtre",
|
"label.newWindow": "Nouvelle fenêtre",
|
||||||
"label.fullscreen" : " Plein écran",
|
"label.fullscreen": "Plein écran",
|
||||||
"label.openDrawer" : " Ouvrir Drawer",
|
"label.openDrawer": "Ouvrir Drawer",
|
||||||
"label.leave" : " Quiter",
|
"label.leave": "Quitter",
|
||||||
"label.chatInput" : " Entrer un message",
|
"label.chatInput": "Entrer un message",
|
||||||
"label.chat" : " Chat",
|
"label.chat": "Chat",
|
||||||
"label.filesharing" : " Partage de fichier",
|
"label.filesharing": "Partage de fichier",
|
||||||
"label.participants" : " Participants",
|
"label.participants": "Participants",
|
||||||
"label.shareFile" : " Partager un fichier",
|
"label.shareFile": "Partager un fichier",
|
||||||
"label.fileSharingUnsupported" : " Partage de fichier non supporté",
|
"label.shareGalleryFile": null,
|
||||||
"label.unknown" : " Inconnu",
|
"label.fileSharingUnsupported": "Partage de fichier non supporté",
|
||||||
"label.democratic" : " Vue démocratique",
|
"label.unknown": "Inconnu",
|
||||||
"label.filmstrip" : " Vue avec miniature",
|
"label.democratic": "Vue démocratique",
|
||||||
"label.low" : " Basse définition",
|
"label.filmstrip": "Vue avec miniature",
|
||||||
"label.medium" : " Définition normale",
|
"label.low": "Basse définition",
|
||||||
"label.high" : " Haute Définition (HD)",
|
"label.medium": "Définition normale",
|
||||||
"label.veryHigh" : " Très Haute Définition (FHD)",
|
"label.high": "Haute Définition (HD)",
|
||||||
"label.ultra" : " Ultra Haute Définition",
|
"label.veryHigh": "Très Haute Définition (FHD)",
|
||||||
"label.close" : " Fermer",
|
"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.settings": "Paramètres",
|
||||||
"settings.camera" : " Caméra",
|
"settings.camera": "Caméra",
|
||||||
"settings.selectCamera" : " Sélectionner votre caméra",
|
"settings.selectCamera": "Sélectionnez votre caméra",
|
||||||
"settings.cantSelectCamera" : " Impossible de sélectionner votre caméra",
|
"settings.cantSelectCamera": "Impossible de sélectionner votre caméra",
|
||||||
"settings.audio" : " Microphone",
|
"settings.audio": "Microphone",
|
||||||
"settings.selectAudio" : " Sélectionner votre microphone",
|
"settings.selectAudio": "Sélectionnez votre microphone",
|
||||||
"settings.cantSelectAudio" : " Impossible de sélectionner votre la caméra",
|
"settings.cantSelectAudio": "Impossible de sélectionner votre la caméra",
|
||||||
"settings.resolution" : " Sélection votre résolution",
|
"settings.audioOutput": "Périphérique de sortie audio",
|
||||||
"settings.layout" : " Mode d'affichage de la salle",
|
"settings.selectAudioOutput": "Sélectionnez le périphérique de sortie audio",
|
||||||
"settings.selectRoomLayout" : " Sélectionner l'affiche de la salle",
|
"settings.cantSelectAudioOutput": "Impossible de sélectionner le périphérique de sortie audio",
|
||||||
"settings.advancedMode" : " Mode avancé",
|
"settings.resolution": "Sélectionnez votre résolution",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
|
"settings.layout": "Mode d'affichage de la salle",
|
||||||
|
"settings.selectRoomLayout": "Sélectionnez la présentation de la salle",
|
||||||
|
"settings.advancedMode": "Mode avancé",
|
||||||
"settings.permanentTopBar": "Barre supérieure permanente",
|
"settings.permanentTopBar": "Barre supérieure permanente",
|
||||||
"settings.lastn": "Nombre de vidéos visibles",
|
"settings.lastn": "Nombre de vidéos visibles",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
"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.saveFileError": "Impossible d'enregistrer le fichier",
|
||||||
"filesharing.startingFileShare" : " Début du transfert de fichier",
|
"filesharing.startingFileShare": "Début du transfert de fichier",
|
||||||
"filesharing.successfulFileShare" : " Fichier transféré",
|
"filesharing.successfulFileShare": "Fichier transféré",
|
||||||
"filesharing.unableToShare" : " Impossible de transférer le fichier",
|
"filesharing.unableToShare": "Impossible de transférer le fichier",
|
||||||
"filesharing.error" : " Erreur lors du transfert de fichier",
|
"filesharing.error": "Erreur lors du transfert de fichier",
|
||||||
"filesharing.finished" : " Fin du transfert de fichier",
|
"filesharing.finished": "Fin du transfert de fichier",
|
||||||
"filesharing.save" : " Sauver",
|
"filesharing.save": "Sauver",
|
||||||
"filesharing.sharedFile" : " {displayName} a partagé un fichier",
|
"filesharing.sharedFile": "{displayName} a partagé un fichier",
|
||||||
"filesharing.download" : " Télécharger",
|
"filesharing.download": "Télécharger",
|
||||||
"filesharing.missingSeeds" : " Si le téléchargement prend trop de temps c’est qu’il n’y a peut-être plus personne qui partage ce torrent. Demander à quelqu’un de repartager le document.",
|
"filesharing.missingSeeds": "Si le téléchargement prend trop de temps c’est qu’il n’y a peut-être plus personne qui partage ce torrent. Demander à quelqu’un de repartager le document.",
|
||||||
"devices.devicesChanged" : " Vos périphériques ont changé, reconfigurer vos périphériques avec le menu paramètre",
|
"devices.devicesChanged": "Vos périphériques ont changé, reconfigurez vos périphériques dans le menu paramètre",
|
||||||
|
|
||||||
"device.audioUnsupported" : " Microphone non supporté",
|
"device.audioUnsupported": "Microphone non supporté",
|
||||||
"device.activateAudio" : " Activer l'audio",
|
"device.activateAudio": "Activer l'audio",
|
||||||
"device.muteAudio" : " Désactiver l'audio",
|
"device.muteAudio": "Désactiver l'audio",
|
||||||
"device.unMuteAudio" : " Réactiver l'audio",
|
"device.unMuteAudio": "Réactiver l'audio",
|
||||||
|
|
||||||
"device.videoUnsupported" : " Vidéo non supporté",
|
"device.videoUnsupported": "Vidéo non supportée",
|
||||||
"device.startVideo" : " Démarrer la vidéo",
|
"device.startVideo": "Démarrer la vidéo",
|
||||||
"device.stopVideo" : " Arrêter la vidéo",
|
"device.stopVideo": "Arrêter la vidéo",
|
||||||
|
|
||||||
"device.screenSharingUnsupported" : " Partage d'écran non supporté",
|
"device.screenSharingUnsupported": "Partage d'écran non supporté",
|
||||||
"device.startScreenSharing" : " Démarrer le partage d 'écran'",
|
"device.startScreenSharing": "Démarrer le partage d 'écran'",
|
||||||
"device.stopScreenSharing" : " Arrêter le partage d'écran",
|
"device.stopScreenSharing": "Arrêter le partage d'écran",
|
||||||
|
|
||||||
"devices.microphoneDisconnected" : " Microphone déconnecté",
|
"devices.microphoneDisconnected": "Microphone déconnecté",
|
||||||
"devices.microphoneError" : " Une erreur est apparue lors de l'accès à votre microphone",
|
"devices.microphoneError": "Une erreur est apparue lors de l'accès à votre microphone",
|
||||||
"devices.microPhoneMute" : " Désactiver le microphone",
|
"devices.microphoneMute": "Désactiver le microphone",
|
||||||
"devices.micophoneUnMute" : " Réactiver le microphone",
|
"devices.microphoneUnMute": "Réactiver le microphone",
|
||||||
"devices.microphoneEnable" : " Activer le microphone",
|
"devices.microphoneEnable": "Activer le microphone",
|
||||||
"devices.microphoneMuteError" : " Impossible de désactiver le microphone",
|
"devices.microphoneMuteError": "Impossible de désactiver le microphone",
|
||||||
"devices.microphoneUnMuteError" : " Impossible de réactiver le microphone",
|
"devices.microphoneUnMuteError": "Impossible de réactiver le microphone",
|
||||||
|
|
||||||
"devices.screenSharingDisconnected" : " Partage d'écran déconnecté",
|
"devices.screenSharingDisconnected": "Partage d'écran déconnecté",
|
||||||
"devices.screenSharingError" : " Une erreur est apparue lors de l'accès à votre partage d'écran",
|
"devices.screenSharingError": "Une erreur est apparue lors de l'accès à votre partage d'écran",
|
||||||
|
|
||||||
"devices.cameraDisconnected" : " Caméra déconnectée",
|
"devices.cameraDisconnected": "Caméra déconnectée",
|
||||||
"devices.cameraError" : " Une erreur est apparue lors de l'accès à votre caméra"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,36 @@
|
||||||
"room.audioVideo": "Zvuk i slika",
|
"room.audioVideo": "Zvuk i slika",
|
||||||
"room.youAreReady": "Spremni ste",
|
"room.youAreReady": "Spremni ste",
|
||||||
"room.emptyRequireLogin": "Soba je trenutno prazna! Prijavite se za pokretanje sastanka, ili sačekajte organizatora" ,
|
"room.emptyRequireLogin": "Soba je trenutno prazna! Prijavite se za pokretanje sastanka, ili sačekajte organizatora" ,
|
||||||
"room.locketWait": "Soba je zaključana - pričekajte odobrenje ...",
|
"room.locketWait": "Soba je zaključana - pričekajte odobrenje...",
|
||||||
"room.lobbyAdministration":"Upravljanje predvorjem",
|
"room.lobbyAdministration":"Upravljanje predvorjem",
|
||||||
"room.peersInLobby":"Sudionici u predvorju",
|
"room.peersInLobby":"Sudionici u predvorju",
|
||||||
"room.lobbyEmpty": "Trenutno nema nikoga u predvorju",
|
"room.lobbyEmpty": "Trenutno nema nikoga u predvorju",
|
||||||
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
|
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
|
||||||
"room.me": "Ja",
|
"room.me": "Ja",
|
||||||
"room.spotlights": "Sudionici u fokusu",
|
"room.spotlights": "Sudionici u središtu pažnje",
|
||||||
"room.passive": "Pasivni sudionici",
|
"room.passive": "Pasivni sudionici",
|
||||||
"room.videoPaused": "Video pauziran",
|
"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.login": "Prijava",
|
||||||
"tooltip.logout": "Odjava",
|
"tooltip.logout": "Odjava",
|
||||||
|
|
@ -59,7 +80,15 @@
|
||||||
"tooltip.leaveFullscreen": "Izađi iz punog ekrana",
|
"tooltip.leaveFullscreen": "Izađi iz punog ekrana",
|
||||||
"tooltip.lobby": "Prikaži predvorje",
|
"tooltip.lobby": "Prikaži predvorje",
|
||||||
"tooltip.settings": "Prikaži postavke",
|
"tooltip.settings": "Prikaži postavke",
|
||||||
"tooltip.participants": "Pokažite sudionike",
|
"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",
|
||||||
|
|
||||||
"label.roomName": "Naziv sobe",
|
"label.roomName": "Naziv sobe",
|
||||||
"label.chooseRoomButton": "Nastavi",
|
"label.chooseRoomButton": "Nastavi",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Dijeljenje datoteka",
|
"label.filesharing": "Dijeljenje datoteka",
|
||||||
"label.participants": "Sudionici",
|
"label.participants": "Sudionici",
|
||||||
"label.shareFile": "Dijeli datoteku",
|
"label.shareFile": "Dijeli datoteku",
|
||||||
|
"label.shareGalleryFile": "Dijeli sliku",
|
||||||
"label.fileSharingUnsupported": "Dijeljenje datoteka nije podržano",
|
"label.fileSharingUnsupported": "Dijeljenje datoteka nije podržano",
|
||||||
"label.unknown": "Nepoznato",
|
"label.unknown": "Nepoznato",
|
||||||
"label.democratic":"Demokratski prikaz",
|
"label.democratic":"Demokratski prikaz",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Vrlo visoka (FHD)",
|
"label.veryHigh": "Vrlo visoka (FHD)",
|
||||||
"label.ultra": "Ultra visoka (UHD)",
|
"label.ultra": "Ultra visoka (UHD)",
|
||||||
"label.close": "Zatvori",
|
"label.close": "Zatvori",
|
||||||
|
"label.media": "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.settings": "Postavke",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Uređaj za zvuk",
|
"settings.audio": "Uređaj za zvuk",
|
||||||
"settings.selectAudio": "Odaberi uređaj za zvuk",
|
"settings.selectAudio": "Odaberi uređaj za zvuk",
|
||||||
"settings.cantSelectAudio": "Nije moguće odabrati 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.resolution": "Odaberi video rezoluciju",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Način prikaza",
|
"settings.layout": "Način prikaza",
|
||||||
"settings.selectRoomLayout": "Odaberi način prikaza",
|
"settings.selectRoomLayout": "Odaberi način prikaza",
|
||||||
"settings.advancedMode": "Napredne mogućnosti",
|
"settings.advancedMode": "Napredne mogućnosti",
|
||||||
"settings.permanentTopBar": "Stalna gornja šipka",
|
"settings.permanentTopBar": "Stalna gornja šipka",
|
||||||
"settings.lastn": "Broj vidljivih videozapisa",
|
"settings.lastn": "Broj vidljivih videozapisa",
|
||||||
|
"settings.hiddenControls": "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.saveFileError": "Nije moguće spremiti datoteku",
|
||||||
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
|
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Mikrofon odspojen",
|
"devices.microphoneDisconnected": "Mikrofon odspojen",
|
||||||
"devices.microphoneError": "Greška prilikom pristupa mikrofonu",
|
"devices.microphoneError": "Greška prilikom pristupa mikrofonu",
|
||||||
"devices.microPhoneMute": "Mikrofon utišan",
|
"devices.microphoneMute": "Mikrofon utišan",
|
||||||
"devices.micophoneUnMute": "Mikrofon pojačan",
|
"devices.microphoneUnMute": "Mikrofon pojačan",
|
||||||
"devices.microphoneEnable": "Mikrofon omogućen",
|
"devices.microphoneEnable": "Mikrofon omogućen",
|
||||||
"devices.microphoneMuteError": "Nije moguće utišati mikrofon",
|
"devices.microphoneMuteError": "Nije moguće utišati mikrofon",
|
||||||
"devices.microphoneUnMuteError": "Nije moguće pojačati mikrofon",
|
"devices.microphoneUnMuteError": "Nije moguće pojačati mikrofon",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Greška prilikom pristupa ekranu",
|
"devices.screenSharingError": "Greška prilikom pristupa ekranu",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Kamera odspojena",
|
"devices.cameraDisconnected": "Kamera odspojena",
|
||||||
"devices.cameraError": "Greška prilikom pristupa kameri"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected": "A kapcsolat lebomlott",
|
"socket.disconnected": "A kapcsolat lebomlott",
|
||||||
"socket.reconnecting": "A kapcsolat lebomlott, újrapróbálkozás",
|
"socket.reconnecting": "A kapcsolat lebomlott, újrapróbálkozás",
|
||||||
"socket.reconnected": "Sikeres újarkapcsolódás",
|
"socket.reconnected": "Sikeres újrakapcsolódás",
|
||||||
"socket.requestError": "Sikertelen szerver lekérés",
|
"socket.requestError": "Sikertelen szerver lekérés",
|
||||||
|
|
||||||
"room.chooseRoom": "Choose the name of the room you would like to join",
|
"room.chooseRoom": "Válaszd ki a konferenciaszobát",
|
||||||
"room.cookieConsent": "Ez a weblap a felhasználói élmény fokozása miatt sütiket használ",
|
"room.cookieConsent": "Ez a weblap a felhasználói élmény fokozása miatt sütiket használ",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "Megértettem",
|
||||||
"room.joined": "Csatlakozátál a konferenciához",
|
"room.joined": "Csatlakoztál a konferenciához",
|
||||||
"room.cantJoin": "Sikertelen csatlakozás a konferenciához",
|
"room.cantJoin": "Sikertelen csatlakozás a konferenciához",
|
||||||
"room.youLocked": "A konferenciába való belépés letiltva",
|
"room.youLocked": "A konferenciába való belépés letiltva",
|
||||||
"room.cantLock": "Sikertelen a konferenciaba való belépés letiltása",
|
"room.cantLock": "Sikertelen a konferenciába való belépés letiltása",
|
||||||
"room.youUnLocked": "A konferenciába való belépés engedélyezve",
|
"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.cantUnLock": "Sikertelen a konferenciába való belépés engedélyezése",
|
||||||
"room.locked": "A konferenciába való belépés letiltva",
|
"room.locked": "A konferenciába való belépés letiltva",
|
||||||
"room.unlocked": "A konferenciába való belépés engedélyezve",
|
"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.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.lobbyPeerLeft": "A konferencia előszobájából a részvevő távozott",
|
||||||
"room.lobbyPeerChangedDisplayName": "Az előszobai résztvevő meváltoztatta a nevét: {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Az előszobai résztvevő megváltoztatta a nevét: {displayName}",
|
||||||
"room.lobbyPeerChangedPicture": "Az előszobai résztvevő meváltoztatta a képét",
|
"room.lobbyPeerChangedPicture": "Az előszobai résztvevő megváltoztatta a képét",
|
||||||
"room.setAccessCode": "A konferencia hozzáférési kódja megváltozott",
|
"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.accessCodeOn": "A konferencia hozzáférési kódja aktiválva",
|
||||||
"room.accessCodeOff": "A konferencia hozzáférési kódka deaktiválva",
|
"room.accessCodeOff": "A konferencia hozzáférési kódka deaktiválva",
|
||||||
|
|
@ -39,8 +39,8 @@
|
||||||
"room.audioOnly": "csak Hang",
|
"room.audioOnly": "csak Hang",
|
||||||
"room.audioVideo": "Hang és Videó",
|
"room.audioVideo": "Hang és Videó",
|
||||||
"room.youAreReady": "Ok, kész vagy",
|
"room.youAreReady": "Ok, kész vagy",
|
||||||
"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.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": "A konferencia szobába a a belépés tilos - Várj amíg valaki be nem enged ...",
|
"room.locketWait": "Az automatikus belépés tiltva van - Várj amíg valaki beenged ...",
|
||||||
"room.lobbyAdministration": "Előszoba adminisztráció",
|
"room.lobbyAdministration": "Előszoba adminisztráció",
|
||||||
"room.peersInLobby": "Résztvevők az előszobában",
|
"room.peersInLobby": "Résztvevők az előszobában",
|
||||||
"room.lobbyEmpty": "Épp senki sincs a konferencia előszobájában",
|
"room.lobbyEmpty": "Épp senki sincs a konferencia előszobájában",
|
||||||
|
|
@ -49,10 +49,31 @@
|
||||||
"room.spotlights": "Látható résztvevők",
|
"room.spotlights": "Látható résztvevők",
|
||||||
"room.passive": "Passzív résztvevők",
|
"room.passive": "Passzív résztvevők",
|
||||||
"room.videoPaused": "Ez a videóstream szünetel",
|
"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.login": "Belépés",
|
||||||
"tooltip.logout": "Kilépés",
|
"tooltip.logout": "Kilépés",
|
||||||
"tooltip.admitFromLobby": "Beenegdem az előszobából",
|
"tooltip.admitFromLobby": "Beengedem az előszobából",
|
||||||
"tooltip.lockRoom": "A konferenciába való belépés letiltása",
|
"tooltip.lockRoom": "A konferenciába való belépés letiltása",
|
||||||
"tooltip.unLockRoom": "konferenciába való belépés engedélyezése",
|
"tooltip.unLockRoom": "konferenciába való belépés engedélyezése",
|
||||||
"tooltip.enterFullscreen": "Teljes képernyős mód",
|
"tooltip.enterFullscreen": "Teljes képernyős mód",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Az előszobában várakozók listája",
|
"tooltip.lobby": "Az előszobában várakozók listája",
|
||||||
"tooltip.settings": "Beállítások",
|
"tooltip.settings": "Beállítások",
|
||||||
"tooltip.participants": "Résztvevők",
|
"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.roomName": "Konferencia",
|
||||||
"label.chooseRoomButton": "Tovább",
|
"label.chooseRoomButton": "Tovább",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Fájl megosztás",
|
"label.filesharing": "Fájl megosztás",
|
||||||
"label.participants": "Résztvevők",
|
"label.participants": "Résztvevők",
|
||||||
"label.shareFile": "Fájl megosztása",
|
"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.fileSharingUnsupported": "Fájl megosztás nem támogatott",
|
||||||
"label.unknown": "Ismeretlen",
|
"label.unknown": "Ismeretlen",
|
||||||
"label.democratic": "Egyforma képméretű képkiosztás",
|
"label.democratic": "Egyforma képméretű képkiosztás",
|
||||||
|
|
@ -83,25 +113,50 @@
|
||||||
"label.veryHigh": "Nagyon magas (FHD)",
|
"label.veryHigh": "Nagyon magas (FHD)",
|
||||||
"label.ultra": "Ultra magas (UHD)",
|
"label.ultra": "Ultra magas (UHD)",
|
||||||
"label.close": "Bezár",
|
"label.close": "Bezár",
|
||||||
|
"label.media": "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.settings": "Beállítások",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
"settings.selectCamera": "Válasz videóeszközt",
|
"settings.selectCamera": "Válassz videoeszközt",
|
||||||
"settings.cantSelectCamera": "Nem lehet a videó eszközt kiválasztani",
|
"settings.cantSelectCamera": "Nem lehet a videoeszközt kiválasztani",
|
||||||
"settings.audio": "Hang eszköz",
|
"settings.audio": "Hang eszköz",
|
||||||
"settings.selectAudio": "Válasz hangeszközt",
|
"settings.selectAudio": "Válassz hangeszközt",
|
||||||
"settings.cantSelectAudio": "Nem lehet a hang eszközt kiválasztani",
|
"settings.cantSelectAudio": "Nem sikerült a hangeszközt kiválasztani",
|
||||||
"settings.resolution": "Válaszd ki a videóeszközöd felbontását",
|
"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.layout": "A konferencia képkiosztása",
|
"settings.layout": "A konferencia képkiosztása",
|
||||||
"settings.selectRoomLayout": "Válaszd ki a konferencia képkiosztását",
|
"settings.selectRoomLayout": "Válaszd ki a konferencia képkiosztását",
|
||||||
"settings.advancedMode": "Részletes információk",
|
"settings.advancedMode": "Részletes információk",
|
||||||
"settings.permanentTopBar": "Állandó felső sáv",
|
"settings.permanentTopBar": "Állandó felső sáv",
|
||||||
"settings.lastn": "A látható videók száma",
|
"settings.lastn": "A látható videók száma",
|
||||||
|
"settings.hiddenControls": "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.saveFileError": "A file-t nem sikerült elmenteni",
|
||||||
"filesharing.startingFileShare": "Fájl megosztása",
|
"filesharing.startingFileShare": "Fájl megosztása",
|
||||||
"filesharing.successfulFileShare": "A fájl sikeresen megosztva",
|
"filesharing.successfulFileShare": "A fájl sikeresen megosztva",
|
||||||
"filesharing.unableToShare": "Sikereteln fájl megosztás",
|
"filesharing.unableToShare": "Sikertelen fájl megosztás",
|
||||||
"filesharing.error": "Hiba a fájlmegosztás során",
|
"filesharing.error": "Hiba a fájlmegosztás során",
|
||||||
"filesharing.finished": "A fájl letöltés befejeződött",
|
"filesharing.finished": "A fájl letöltés befejeződött",
|
||||||
"filesharing.save": "Mentés",
|
"filesharing.save": "Mentés",
|
||||||
|
|
@ -111,7 +166,7 @@
|
||||||
|
|
||||||
"devices.devicesChanged": "Az eszközei megváltoztak, konfiguráld őket be a beállítások menüben",
|
"devices.devicesChanged": "Az eszközei megváltoztak, konfiguráld őket be a beállítások menüben",
|
||||||
|
|
||||||
"device.audioUnsupported": "A hnag nem támogatott",
|
"device.audioUnsupported": "A hang nem támogatott",
|
||||||
"device.activateAudio": "Hang aktiválása",
|
"device.activateAudio": "Hang aktiválása",
|
||||||
"device.muteAudio": "Hang némítása",
|
"device.muteAudio": "Hang némítása",
|
||||||
"device.unMuteAudio": "Hang némítás kikapcsolása",
|
"device.unMuteAudio": "Hang némítás kikapcsolása",
|
||||||
|
|
@ -122,12 +177,12 @@
|
||||||
|
|
||||||
"device.screenSharingUnsupported": "A képernyő megosztás nem támogatott",
|
"device.screenSharingUnsupported": "A képernyő megosztás nem támogatott",
|
||||||
"device.startScreenSharing": "Képernyőmegosztás indítása",
|
"device.startScreenSharing": "Képernyőmegosztás indítása",
|
||||||
"device.stopScreenSharing": "Képernyőmegosztás leáłłítása",
|
"device.stopScreenSharing": "Képernyőmegosztás leállítása",
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Microphone kapcsolat bontva",
|
"devices.microphoneDisconnected": "Mikrofon kapcsolat bontva",
|
||||||
"devices.microphoneError": "Hiba történt a mikrofon hangeszköz elérése közben",
|
"devices.microphoneError": "Hiba történt a mikrofon hangeszköz elérése közben",
|
||||||
"devices.microPhoneMute": "A mikrofon némítva lett",
|
"devices.microphoneMute": "A mikrofon némítva lett",
|
||||||
"devices.micophoneUnMute": "A mikrofon némítása ki lett kapocsolva",
|
"devices.microphoneUnMute": "A mikrofon némítása ki lett kapocsolva",
|
||||||
"devices.microphoneEnable": "A mikrofon engedéylezve",
|
"devices.microphoneEnable": "A mikrofon engedéylezve",
|
||||||
"devices.microphoneMuteError": "Nem sikerült a mikrofonod némítása",
|
"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",
|
"devices.microphoneUnMuteError": "Nem sikerült a mikrofonod némításának kikapcsolása",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Hiba történt a képernyőd megosztása során",
|
"devices.screenSharingError": "Hiba történt a képernyőd megosztása során",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "A kamera kapcsolata lebomlott",
|
"devices.cameraDisconnected": "A kamera kapcsolata lebomlott",
|
||||||
"devices.cameraError": "Hiba történt a kamera elérése során"
|
"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:"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,31 @@
|
||||||
"room.spotlights": "Partecipanti in Evidenza",
|
"room.spotlights": "Partecipanti in Evidenza",
|
||||||
"room.passive": "Participanti Passivi",
|
"room.passive": "Participanti Passivi",
|
||||||
"room.videoPaused": "Il video è in pausa",
|
"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.login": "Log in",
|
||||||
"tooltip.logout": "Log out",
|
"tooltip.logout": "Log out",
|
||||||
"tooltip.admitFromLobby": "Ammetti dalla lobby",
|
"tooltip.admitFromLobby": "Accetta partecipante dalla lobby",
|
||||||
"tooltip.lockRoom": "Blocca stanza",
|
"tooltip.lockRoom": "Blocca stanza",
|
||||||
"tooltip.unLockRoom": "Sblocca stanza",
|
"tooltip.unLockRoom": "Sblocca stanza",
|
||||||
"tooltip.enterFullscreen": "Modalità schermo intero",
|
"tooltip.enterFullscreen": "Modalità schermo intero",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Mostra lobby",
|
"tooltip.lobby": "Mostra lobby",
|
||||||
"tooltip.settings": "Mostra impostazioni",
|
"tooltip.settings": "Mostra impostazioni",
|
||||||
"tooltip.participants": "Mostra partecipanti",
|
"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.roomName": "Nome della stanza",
|
||||||
"label.chooseRoomButton": "Continua",
|
"label.chooseRoomButton": "Continua",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Condivisione file",
|
"label.filesharing": "Condivisione file",
|
||||||
"label.participants": "Partecipanti",
|
"label.participants": "Partecipanti",
|
||||||
"label.shareFile": "Condividi file",
|
"label.shareFile": "Condividi file",
|
||||||
|
"label.shareGalleryFile": "Condividi immagine",
|
||||||
"label.fileSharingUnsupported": "Condivisione file non supportata",
|
"label.fileSharingUnsupported": "Condivisione file non supportata",
|
||||||
"label.unknown": "Sconosciuto",
|
"label.unknown": "Sconosciuto",
|
||||||
"label.democratic": "Vista Democratica",
|
"label.democratic": "Vista Democratica",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Molto alta (FHD)",
|
"label.veryHigh": "Molto alta (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Chiudi",
|
"label.close": "Chiudi",
|
||||||
|
"label.media": "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.settings": "Impostazioni",
|
||||||
"settings.camera": "Videocamera",
|
"settings.camera": "Videocamera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Dispositivo audio",
|
"settings.audio": "Dispositivo audio",
|
||||||
"settings.selectAudio": "Seleziona dispositivo audio",
|
"settings.selectAudio": "Seleziona dispositivo audio",
|
||||||
"settings.cantSelectAudio": "Impossibile selezionare 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.resolution": "Seleziona risoluzione",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Aspetto stanza",
|
"settings.layout": "Aspetto stanza",
|
||||||
"settings.selectRoomLayout": "Seleziona aspetto stanza",
|
"settings.selectRoomLayout": "Seleziona aspetto stanza",
|
||||||
"settings.advancedMode": "Modalità avanzata",
|
"settings.advancedMode": "Modalità avanzata",
|
||||||
"settings.permanentTopBar": "Barra superiore permanente",
|
"settings.permanentTopBar": "Barra superiore permanente",
|
||||||
"settings.lastn": "Numero di video visibili",
|
"settings.lastn": "Numero di video visibili",
|
||||||
|
"settings.hiddenControls": "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.saveFileError": "Impossibile salvare file",
|
||||||
"filesharing.startingFileShare": "Tentativo di condivisione file",
|
"filesharing.startingFileShare": "Tentativo di condivisione file",
|
||||||
|
|
@ -112,7 +167,7 @@
|
||||||
"devices.devicesChanged": "Il tuo dispositivo è cambiato, configura i dispositivi nel menù di impostazioni",
|
"devices.devicesChanged": "Il tuo dispositivo è cambiato, configura i dispositivi nel menù di impostazioni",
|
||||||
|
|
||||||
"device.audioUnsupported": "Dispositivo audio non supportato",
|
"device.audioUnsupported": "Dispositivo audio non supportato",
|
||||||
"device.activateAudio": "Attiva audio",
|
"device.activateAudio": "Attiva audio",
|
||||||
"device.muteAudio": "Silenzia audio",
|
"device.muteAudio": "Silenzia audio",
|
||||||
"device.unMuteAudio": "Riattiva audio",
|
"device.unMuteAudio": "Riattiva audio",
|
||||||
|
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Microfono scollegato",
|
"devices.microphoneDisconnected": "Microfono scollegato",
|
||||||
"devices.microphoneError": "Errore con l'accesso al microfono",
|
"devices.microphoneError": "Errore con l'accesso al microfono",
|
||||||
"devices.microPhoneMute": "Microfono silenziato",
|
"devices.microphoneMute": "Microfono silenziato",
|
||||||
"devices.micophoneUnMute": "Microfono riattivato",
|
"devices.microphoneUnMute": "Microfono riattivato",
|
||||||
"devices.microphoneEnable": "Microfono attivo",
|
"devices.microphoneEnable": "Microfono attivo",
|
||||||
"devices.microphoneMuteError": "Impossibile silenziare il microfono",
|
"devices.microphoneMuteError": "Impossibile silenziare il microfono",
|
||||||
"devices.microphoneUnMuteError": "Impossibile riattivare il microfono",
|
"devices.microphoneUnMuteError": "Impossibile riattivare il microfono",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Errore con l'accesso al tuo schermo",
|
"devices.screenSharingError": "Errore con l'accesso al tuo schermo",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Videocamera scollegata",
|
"devices.cameraDisconnected": "Videocamera scollegata",
|
||||||
"devices.cameraError": "Errore con l'accesso alla videocamera"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Deltakere i fokus",
|
"room.spotlights": "Deltakere i fokus",
|
||||||
"room.passive": "Passive deltakere",
|
"room.passive": "Passive deltakere",
|
||||||
"room.videoPaused": "Denne videoen er inaktiv",
|
"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.login": "Logg in",
|
||||||
"tooltip.logout": "Logg ut",
|
"tooltip.logout": "Logg ut",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Vis lobby",
|
"tooltip.lobby": "Vis lobby",
|
||||||
"tooltip.settings": "Vis innstillinger",
|
"tooltip.settings": "Vis innstillinger",
|
||||||
"tooltip.participants": "Vis deltakere",
|
"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.roomName": "Møtenavn",
|
||||||
"label.chooseRoomButton": "Fortsett",
|
"label.chooseRoomButton": "Fortsett",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Fildeling",
|
"label.filesharing": "Fildeling",
|
||||||
"label.participants": "Deltakere",
|
"label.participants": "Deltakere",
|
||||||
"label.shareFile": "Del fil",
|
"label.shareFile": "Del fil",
|
||||||
|
"label.shareGalleryFile": "Del bilde",
|
||||||
"label.fileSharingUnsupported": "Fildeling ikke støttet",
|
"label.fileSharingUnsupported": "Fildeling ikke støttet",
|
||||||
"label.unknown": "Ukjent",
|
"label.unknown": "Ukjent",
|
||||||
"label.democratic": "Demokratisk",
|
"label.democratic": "Demokratisk",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Veldig høy (FHD)",
|
"label.veryHigh": "Veldig høy (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Lukk",
|
"label.close": "Lukk",
|
||||||
|
"label.media": "Media",
|
||||||
|
"label.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.settings": "Innstillinger",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Lydenhet",
|
"settings.audio": "Lydenhet",
|
||||||
"settings.selectAudio": "Velg lydenhet",
|
"settings.selectAudio": "Velg lydenhet",
|
||||||
"settings.cantSelectAudio": "Kan ikke velge 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.resolution": "Velg oppløsning",
|
||||||
|
"settings.frameRate": "Velg bildefrekvens",
|
||||||
|
"settings.screenSharingResolution": "Velg skjermdelingsoppløsning",
|
||||||
|
"settings.screenSharingFrameRate": "Velg skjermdelingsbildefrekvens",
|
||||||
"settings.layout": "Møtelayout",
|
"settings.layout": "Møtelayout",
|
||||||
"settings.selectRoomLayout": "Velg møtelayout",
|
"settings.selectRoomLayout": "Velg møtelayout",
|
||||||
"settings.advancedMode": "Avansert modus",
|
"settings.advancedMode": "Avansert modus",
|
||||||
"settings.permanentTopBar": "Permanent topplinje",
|
"settings.permanentTopBar": "Permanent topplinje",
|
||||||
"settings.lastn": "Antall videoer synlig",
|
"settings.lastn": "Antall videoer synlig",
|
||||||
|
"settings.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.saveFileError": "Klarte ikke å lagre fil",
|
||||||
"filesharing.startingFileShare": "Starter fildeling",
|
"filesharing.startingFileShare": "Starter fildeling",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Mikrofon koblet fra",
|
"devices.microphoneDisconnected": "Mikrofon koblet fra",
|
||||||
"devices.microphoneError": "Det skjedde noe feil med mikrofonen din",
|
"devices.microphoneError": "Det skjedde noe feil med mikrofonen din",
|
||||||
"devices.microPhoneMute": "Dempet mikrofonen",
|
"devices.microphoneMute": "Dempet mikrofonen",
|
||||||
"devices.micophoneUnMute": "Aktiverte mikrofonen",
|
"devices.microphoneUnMute": "Aktiverte mikrofonen",
|
||||||
"devices.microphoneEnable": "Aktiverte mikrofonen",
|
"devices.microphoneEnable": "Aktiverte mikrofonen",
|
||||||
"devices.microphoneMuteError": "Klarte ikke å dempe mikrofonen",
|
"devices.microphoneMuteError": "Klarte ikke å dempe mikrofonen",
|
||||||
"devices.microphoneUnMuteError": "Klarte ikke å aktivere mikrofonen",
|
"devices.microphoneUnMuteError": "Klarte ikke å aktivere mikrofonen",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Det skjedde noe feil med skjermdelingen din",
|
"devices.screenSharingError": "Det skjedde noe feil med skjermdelingen din",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Kamera koblet fra",
|
"devices.cameraDisconnected": "Kamera koblet fra",
|
||||||
"devices.cameraError": "Det skjedde noe feil med kameraet ditt"
|
"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:"
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
"room.chooseRoom": "Wybór konferencji",
|
"room.chooseRoom": "Wybór konferencji",
|
||||||
"room.cookieConsent": "Ta strona internetowa wykorzystuje pliki cookie w celu zwiększenia wygody użytkowania.",
|
"room.cookieConsent": "Ta strona internetowa wykorzystuje pliki cookie w celu zwiększenia wygody użytkowania.",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "Rozumiem",
|
||||||
"room.joined": "Podłączono do konferencji",
|
"room.joined": "Podłączono do konferencji",
|
||||||
"room.cantJoin": "Brak możliwości dołączenia do pokoju",
|
"room.cantJoin": "Brak możliwości dołączenia do pokoju",
|
||||||
"room.youLocked": "Zakluczono pokój",
|
"room.youLocked": "Zakluczono pokój",
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Aktywni uczestnicy",
|
"room.spotlights": "Aktywni uczestnicy",
|
||||||
"room.passive": "Pasywni uczestnicy",
|
"room.passive": "Pasywni uczestnicy",
|
||||||
"room.videoPaused": "To wideo jest wstrzymane.",
|
"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.login": "Zaloguj",
|
||||||
"tooltip.logout": "Wyloguj",
|
"tooltip.logout": "Wyloguj",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Pokaż poczekalnię",
|
"tooltip.lobby": "Pokaż poczekalnię",
|
||||||
"tooltip.settings": "Pokaż ustawienia",
|
"tooltip.settings": "Pokaż ustawienia",
|
||||||
"tooltip.participants": "Pokaż uczestników",
|
"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.roomName": "Nazwa konferencji",
|
||||||
"label.chooseRoomButton": "Kontynuuj",
|
"label.chooseRoomButton": "Kontynuuj",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Udostępnianie plików",
|
"label.filesharing": "Udostępnianie plików",
|
||||||
"label.participants": "Uczestnicy",
|
"label.participants": "Uczestnicy",
|
||||||
"label.shareFile": "Udostępnij plik",
|
"label.shareFile": "Udostępnij plik",
|
||||||
|
"label.shareGalleryFile": "Udostępnij obraz",
|
||||||
"label.fileSharingUnsupported": "Udostępnianie plików nie jest obsługiwane",
|
"label.fileSharingUnsupported": "Udostępnianie plików nie jest obsługiwane",
|
||||||
"label.unknown": "Nieznane",
|
"label.unknown": "Nieznane",
|
||||||
"label.democratic": "Układ demokratyczny",
|
"label.democratic": "Układ demokratyczny",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Bardzo wysoka (FHD)",
|
"label.veryHigh": "Bardzo wysoka (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Zamknij",
|
"label.close": "Zamknij",
|
||||||
|
"label.media": "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.settings": "Ustawienia",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Urządzenie audio",
|
"settings.audio": "Urządzenie audio",
|
||||||
"settings.selectAudio": "Wybór urządzenia audio",
|
"settings.selectAudio": "Wybór urządzenia audio",
|
||||||
"settings.cantSelectAudio": "Nie można wybrać 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.resolution": "Wybór rozdzielczości wideo",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Układ konferencji",
|
"settings.layout": "Układ konferencji",
|
||||||
"settings.selectRoomLayout": "Ustawienia układu konferencji",
|
"settings.selectRoomLayout": "Ustawienia układu konferencji",
|
||||||
"settings.advancedMode": "Tryb zaawansowany",
|
"settings.advancedMode": "Tryb zaawansowany",
|
||||||
"settings.permanentTopBar": "Stały górny pasek",
|
"settings.permanentTopBar": "Stały górny pasek",
|
||||||
"settings.lastn": "Liczba widocznych filmów",
|
"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,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Nie można zapisać pliku",
|
"filesharing.saveFileError": "Nie można zapisać pliku",
|
||||||
"filesharing.startingFileShare": "Próba udostępnienia pliku",
|
"filesharing.startingFileShare": "Próba udostępnienia pliku",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Odłączono mikrofon",
|
"devices.microphoneDisconnected": "Odłączono mikrofon",
|
||||||
"devices.microphoneError": "Błąd dostępu do mikrofonu",
|
"devices.microphoneError": "Błąd dostępu do mikrofonu",
|
||||||
"devices.microPhoneMute": "Wyciszenie mikrofonu włączone",
|
"devices.microphoneMute": "Wyciszenie mikrofonu włączone",
|
||||||
"devices.micophoneUnMute": "Wyciszenie mikrofonu wyłączone",
|
"devices.microphoneUnMute": "Wyciszenie mikrofonu wyłączone",
|
||||||
"devices.microphoneEnable": "Włączono mikrofon",
|
"devices.microphoneEnable": "Włączono mikrofon",
|
||||||
"devices.microphoneMuteError": "Nie można wyciszyć mikrofonu",
|
"devices.microphoneMuteError": "Nie można wyciszyć mikrofonu",
|
||||||
"devices.microphoneUnMuteError": "Nie można wyłączyć wyciszenia mikrofonu.",
|
"devices.microphoneUnMuteError": "Nie można wyłączyć wyciszenia mikrofonu.",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Wystąpił błąd podczas uzyskiwania dostępu do ekranu",
|
"devices.screenSharingError": "Wystąpił błąd podczas uzyskiwania dostępu do ekranu",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Kamera odłączona",
|
"devices.cameraDisconnected": "Kamera odłączona",
|
||||||
"devices.cameraError": "Wystąpił błąd podczas uzyskiwania dostępu do kamery"
|
"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
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Participantes em foco",
|
"room.spotlights": "Participantes em foco",
|
||||||
"room.passive": "Participantes passivos",
|
"room.passive": "Participantes passivos",
|
||||||
"room.videoPaused": "Este vídeo está em pausa",
|
"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.login": "Entrar",
|
||||||
"tooltip.logout": "Sair",
|
"tooltip.logout": "Sair",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Apresentar sala de espera",
|
"tooltip.lobby": "Apresentar sala de espera",
|
||||||
"tooltip.settings": "Apresentar definições",
|
"tooltip.settings": "Apresentar definições",
|
||||||
"tooltip.participants": "Apresentar participantes",
|
"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.roomName": "Nome da sala",
|
||||||
"label.chooseRoomButton": "Continuar",
|
"label.chooseRoomButton": "Continuar",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Partilha de ficheiro",
|
"label.filesharing": "Partilha de ficheiro",
|
||||||
"label.participants": "Participantes",
|
"label.participants": "Participantes",
|
||||||
"label.shareFile": "Partilhar ficheiro",
|
"label.shareFile": "Partilhar ficheiro",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "Partilha de ficheiro não disponível",
|
"label.fileSharingUnsupported": "Partilha de ficheiro não disponível",
|
||||||
"label.unknown": "Desconhecido",
|
"label.unknown": "Desconhecido",
|
||||||
"label.democratic": "Vista democrática",
|
"label.democratic": "Vista democrática",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Muito alta (FHD)",
|
"label.veryHigh": "Muito alta (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Fechar",
|
"label.close": "Fechar",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearance": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
"label.promoteAllPeers": null,
|
||||||
|
"label.moreActions": null,
|
||||||
|
"label.version": null,
|
||||||
|
|
||||||
"settings.settings": "Definições",
|
"settings.settings": "Definições",
|
||||||
"settings.camera": "Camera",
|
"settings.camera": "Camera",
|
||||||
|
|
@ -91,12 +128,30 @@
|
||||||
"settings.audio": "Dispositivo Áudio",
|
"settings.audio": "Dispositivo Áudio",
|
||||||
"settings.selectAudio": "Selecione o seu dispositivo de áudio",
|
"settings.selectAudio": "Selecione o seu dispositivo de áudio",
|
||||||
"settings.cantSelectAudio": "Impossível selecionar 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.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.layout": "Disposição da sala",
|
||||||
"settings.selectRoomLayout": "Seleccione a disposição da sala",
|
"settings.selectRoomLayout": "Seleccione a disposição da sala",
|
||||||
"settings.advancedMode": "Modo avançado",
|
"settings.advancedMode": "Modo avançado",
|
||||||
"settings.permanentTopBar": "Barra superior permanente",
|
"settings.permanentTopBar": "Barra superior permanente",
|
||||||
"settings.lastn": "Número de vídeos visíveis",
|
"settings.lastn": "Número de vídeos visíveis",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
"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.saveFileError": "Impossível de gravar o ficheiro",
|
||||||
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
|
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
|
||||||
|
|
@ -126,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Microfone desiligado",
|
"devices.microphoneDisconnected": "Microfone desiligado",
|
||||||
"devices.microphoneError": "Ocorreu um erro no acesso ao microfone",
|
"devices.microphoneError": "Ocorreu um erro no acesso ao microfone",
|
||||||
"devices.microPhoneMute": "Som microfone desativado",
|
"devices.microphoneMute": "Som microfone desativado",
|
||||||
"devices.micophoneUnMute": "Som mmicrofone ativado",
|
"devices.microphoneUnMute": "Som mmicrofone ativado",
|
||||||
"devices.microphoneEnable": "Microfone ativado",
|
"devices.microphoneEnable": "Microfone ativado",
|
||||||
"devices.microphoneMuteError": "Não foi possível cortar o som do microfone",
|
"devices.microphoneMuteError": "Não foi possível cortar o som do microfone",
|
||||||
"devices.microphoneUnMuteError": "Não foi possível ativar o som do microfone",
|
"devices.microphoneUnMuteError": "Não foi possível ativar o som do microfone",
|
||||||
|
|
@ -136,5 +191,15 @@
|
||||||
"devices.screenSharingError": "Ocorreu um erro no acesso ao seu ecrã",
|
"devices.screenSharingError": "Ocorreu um erro no acesso ao seu ecrã",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Câmara desconectada",
|
"devices.cameraDisconnected": "Câmara desconectada",
|
||||||
"devices.cameraError": "Ocorreu um erro no acesso à sua câmara"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Participanți în Spotlight",
|
"room.spotlights": "Participanți în Spotlight",
|
||||||
"room.passive": "Participanți pasivi",
|
"room.passive": "Participanți pasivi",
|
||||||
"room.videoPaused": "Acest video este pus pe pauză",
|
"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.login": "Intră în cont",
|
||||||
"tooltip.logout": "Deconectare",
|
"tooltip.logout": "Deconectare",
|
||||||
|
|
@ -58,7 +79,16 @@
|
||||||
"tooltip.enterFullscreen": "Modul ecran complet",
|
"tooltip.enterFullscreen": "Modul ecran complet",
|
||||||
"tooltip.leaveFullscreen": "Ieșire din modul ecran complet",
|
"tooltip.leaveFullscreen": "Ieșire din modul ecran complet",
|
||||||
"tooltip.lobby": "Arată holul",
|
"tooltip.lobby": "Arată holul",
|
||||||
"tooltip.settings": "Arată participanții",
|
"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,
|
||||||
|
|
||||||
"label.roomName": "Numele camerei",
|
"label.roomName": "Numele camerei",
|
||||||
"label.chooseRoomButton": "Continuare",
|
"label.chooseRoomButton": "Continuare",
|
||||||
|
|
@ -72,6 +102,7 @@
|
||||||
"label.filesharing": "Partajarea fișierelor",
|
"label.filesharing": "Partajarea fișierelor",
|
||||||
"label.participants": "Participanți",
|
"label.participants": "Participanți",
|
||||||
"label.shareFile": "Partajează fișierul",
|
"label.shareFile": "Partajează fișierul",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "Partajarea fișierelor nu este acceptată",
|
"label.fileSharingUnsupported": "Partajarea fișierelor nu este acceptată",
|
||||||
"label.unknown": "Necunoscut",
|
"label.unknown": "Necunoscut",
|
||||||
"label.democratic": "Distribuție egală a dimensiunii imaginii",
|
"label.democratic": "Distribuție egală a dimensiunii imaginii",
|
||||||
|
|
@ -82,6 +113,13 @@
|
||||||
"label.veryHigh": "Rezoluție foarte înaltă (FHD)",
|
"label.veryHigh": "Rezoluție foarte înaltă (FHD)",
|
||||||
"label.ultra": "Rezoluție ultra înaltă (UHD)",
|
"label.ultra": "Rezoluție ultra înaltă (UHD)",
|
||||||
"label.close": "Închide",
|
"label.close": "Închide",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearance": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
"label.promoteAllPeers": null,
|
||||||
|
"label.moreActions": null,
|
||||||
|
"label.version": null,
|
||||||
|
|
||||||
"settings.settings": "Setări",
|
"settings.settings": "Setări",
|
||||||
"settings.camera": "Cameră video",
|
"settings.camera": "Cameră video",
|
||||||
|
|
@ -90,12 +128,30 @@
|
||||||
"settings.audio": "Dispozitivul audio",
|
"settings.audio": "Dispozitivul audio",
|
||||||
"settings.selectAudio": "Selectarea dispozitivul audio",
|
"settings.selectAudio": "Selectarea dispozitivul audio",
|
||||||
"settings.cantSelectAudio": "Încercarea de a selecta dispozitivul audio a eșuat",
|
"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.resolution": "Selectează rezoluția video",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Aspectul camerei video",
|
"settings.layout": "Aspectul camerei video",
|
||||||
"settings.selectRoomLayout": "Selectează spectul camerei video",
|
"settings.selectRoomLayout": "Selectează spectul camerei video",
|
||||||
"settings.advancedMode": "Mod avansat",
|
"settings.advancedMode": "Mod avansat",
|
||||||
"settings.permanentTopBar": "Bara de sus permanentă",
|
"settings.permanentTopBar": "Bara de sus permanentă",
|
||||||
"settings.lastn": "Numărul de videoclipuri vizibile",
|
"settings.lastn": "Numărul de videoclipuri vizibile",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
"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.saveFileError": "Încercarea de a salva fișierul a eșuat",
|
||||||
"filesharing.startingFileShare": "Partajarea fișierului",
|
"filesharing.startingFileShare": "Partajarea fișierului",
|
||||||
|
|
@ -125,8 +181,8 @@
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Microfonul e deconectat",
|
"devices.microphoneDisconnected": "Microfonul e deconectat",
|
||||||
"devices.microphoneError": "A apărut o eroare la accesarea microfonului",
|
"devices.microphoneError": "A apărut o eroare la accesarea microfonului",
|
||||||
"devices.microPhoneMute": "Microfonul e dezactivat",
|
"devices.microphoneMute": "Microfonul e dezactivat",
|
||||||
"devices.micophoneUnMute": "Retragerea dezactivării microfonului",
|
"devices.microphoneUnMute": "Retragerea dezactivării microfonului",
|
||||||
"devices.microphoneEnable": "Microfonul e activat",
|
"devices.microphoneEnable": "Microfonul e activat",
|
||||||
"devices.microphoneMuteError": "Încercarea de a dezactiva microfonului a eșuat",
|
"devices.microphoneMuteError": "Încercarea de a dezactiva microfonului a eșuat",
|
||||||
"devices.microphoneUnMuteError": "Încercarea de a retrage dezactivarea microfonului a eșuat",
|
"devices.microphoneUnMuteError": "Încercarea de a retrage dezactivarea microfonului a eșuat",
|
||||||
|
|
@ -135,5 +191,15 @@
|
||||||
"devices.screenSharingError": "A apărut o eroare la accesarea ecranului",
|
"devices.screenSharingError": "A apărut o eroare la accesarea ecranului",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Camera video e disconectată",
|
"devices.cameraDisconnected": "Camera video e disconectată",
|
||||||
"devices.cameraError": "A apărut o eroare la accesarea camerei video"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,27 @@
|
||||||
"room.spotlights": "Gündemdeki Katılımcılar",
|
"room.spotlights": "Gündemdeki Katılımcılar",
|
||||||
"room.passive": "Pasif Katılımcılar",
|
"room.passive": "Pasif Katılımcılar",
|
||||||
"room.videoPaused": "Video duraklatıldı",
|
"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.login": "Giriş",
|
||||||
"tooltip.logout": "Çıkış",
|
"tooltip.logout": "Çıkış",
|
||||||
|
|
@ -60,6 +81,14 @@
|
||||||
"tooltip.lobby": "Lobiyi göster",
|
"tooltip.lobby": "Lobiyi göster",
|
||||||
"tooltip.settings": "Ayarları göster",
|
"tooltip.settings": "Ayarları göster",
|
||||||
"tooltip.participants": "Katılımcıları 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.roomName": "Oda adı",
|
||||||
"label.chooseRoomButton": "Devam",
|
"label.chooseRoomButton": "Devam",
|
||||||
|
|
@ -73,6 +102,7 @@
|
||||||
"label.filesharing": "Dosya paylaşım",
|
"label.filesharing": "Dosya paylaşım",
|
||||||
"label.participants": "Katılımcı",
|
"label.participants": "Katılımcı",
|
||||||
"label.shareFile": "Dosya paylaş",
|
"label.shareFile": "Dosya paylaş",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
"label.fileSharingUnsupported": "Dosya paylaşımı desteklenmiyor",
|
"label.fileSharingUnsupported": "Dosya paylaşımı desteklenmiyor",
|
||||||
"label.unknown": "Bilinmeyen",
|
"label.unknown": "Bilinmeyen",
|
||||||
"label.democratic": "Demokratik görünüm",
|
"label.democratic": "Demokratik görünüm",
|
||||||
|
|
@ -83,6 +113,13 @@
|
||||||
"label.veryHigh": "Çok Yüksek (FHD)",
|
"label.veryHigh": "Çok Yüksek (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Kapat",
|
"label.close": "Kapat",
|
||||||
|
"label.media": null,
|
||||||
|
"label.appearance": null,
|
||||||
|
"label.advanced": null,
|
||||||
|
"label.addVideo": null,
|
||||||
|
"label.promoteAllPeers": null,
|
||||||
|
"label.moreActions": null,
|
||||||
|
"label.version": null,
|
||||||
|
|
||||||
"settings.settings": "Ayarlar",
|
"settings.settings": "Ayarlar",
|
||||||
"settings.camera": "Kamera",
|
"settings.camera": "Kamera",
|
||||||
|
|
@ -92,11 +129,26 @@
|
||||||
"settings.selectAudio": "Ses aygıtını seç",
|
"settings.selectAudio": "Ses aygıtını seç",
|
||||||
"settings.cantSelectAudio": "Ses aygıtı seçilemiyor",
|
"settings.cantSelectAudio": "Ses aygıtı seçilemiyor",
|
||||||
"settings.resolution": "Video çözünürlüğü ayarla",
|
"settings.resolution": "Video çözünürlüğü ayarla",
|
||||||
|
"settings.frameRate": null,
|
||||||
|
"settings.screenSharingResolution": null,
|
||||||
|
"settings.screenSharingFrameRate": null,
|
||||||
"settings.layout": "Oda düzeni",
|
"settings.layout": "Oda düzeni",
|
||||||
"settings.selectRoomLayout": "Oda düzeni seç",
|
"settings.selectRoomLayout": "Oda düzeni seç",
|
||||||
"settings.advancedMode": "Detaylı mod",
|
"settings.advancedMode": "Detaylı mod",
|
||||||
"settings.permanentTopBar": "Üst barı kalıcı yap",
|
"settings.permanentTopBar": "Üst barı kalıcı yap",
|
||||||
"settings.lastn": "İzlenebilir video sayısı",
|
"settings.lastn": "İzlenebilir video sayısı",
|
||||||
|
"settings.hiddenControls": null,
|
||||||
|
"settings.notificationSounds": null,
|
||||||
|
"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.saveFileError": "Dosya kaydedilemiyor",
|
||||||
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
|
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
|
||||||
|
|
@ -136,5 +188,15 @@
|
||||||
"devices.screenSharingError": "Ekranınıza erişilirken bir hata oluştu",
|
"devices.screenSharingError": "Ekranınıza erişilirken bir hata oluştu",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Kamera bağlı değil",
|
"devices.cameraDisconnected": "Kamera bağlı değil",
|
||||||
"devices.cameraError": "Kameranıza erişilirken bir hata oluştu"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
@ -1,140 +1,205 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected": "Ви відключені",
|
"socket.disconnected": "Ви відключені",
|
||||||
"socket.reconnecting": "Ви від'єдналися, намагаєтесь знову підключитися",
|
"socket.reconnecting": "Ви від'єдналися, намагаєтесь знову підключитися",
|
||||||
"socket.reconnected": "Ви знову підключилися",
|
"socket.reconnected": "Ви знову підключилися",
|
||||||
"socket.requestError": "Помилка при запиті сервера",
|
"socket.requestError": "Помилка при запиті сервера",
|
||||||
|
|
||||||
"room.chooseRoom": "Виберіть назву кімнати, до якої хочете приєднатися",
|
"room.chooseRoom": "Виберіть назву кімнати, до якої хочете приєднатися",
|
||||||
"room.cookieConsent": "Цей веб-сайт використовує файли cookie для поліпшення роботи користувачів",
|
"room.cookieConsent": "Цей веб-сайт використовує файли cookie для поліпшення роботи користувачів",
|
||||||
"room.consentUnderstand": "Я розумію",
|
"room.consentUnderstand": "Я розумію",
|
||||||
"room.joined": "Ви приєдналися до кімнати",
|
"room.joined": "Ви приєдналися до кімнати",
|
||||||
"room.cantJoin": "Неможливо приєднатися до кімнати",
|
"room.cantJoin": "Неможливо приєднатися до кімнати",
|
||||||
"room.youLocked": "Ви заблокували кімнату",
|
"room.youLocked": "Ви заблокували кімнату",
|
||||||
"room.cantLock": "Не вдається заблокувати кімнату",
|
"room.cantLock": "Не вдається заблокувати кімнату",
|
||||||
"room.youUnLocked": "Ви розблокували кімнату",
|
"room.youUnLocked": "Ви розблокували кімнату",
|
||||||
"room.cantUnLock": "Не вдається розблокувати кімнату",
|
"room.cantUnLock": "Не вдається розблокувати кімнату",
|
||||||
"room.locked": "Кімната зараз заблокована",
|
"room.locked": "Кімната зараз заблокована",
|
||||||
"room.unlocked": "Кімната зараз розблокована",
|
"room.unlocked": "Кімната зараз розблокована",
|
||||||
"room.newLobbyPeer": "Новий учасник увійшов у зал очікування",
|
"room.newLobbyPeer": "Новий учасник увійшов у зал очікування",
|
||||||
"room.lobbyPeerLeft": "Учасник вийшов із зала очікування",
|
"room.lobbyPeerLeft": "Учасник вийшов із зала очікування",
|
||||||
"room.lobbyPeerChangedDisplayName": "Учасник у залі очікування змінив ім'я на {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Учасник у залі очікування змінив ім'я на {displayName}",
|
||||||
"room.lobbyPeerChangedPicture": "Учасник залу очікування змінив зображення",
|
"room.lobbyPeerChangedPicture": "Учасник залу очікування змінив зображення",
|
||||||
"room.setAccessCode": "Код доступу до кімнати оновлений",
|
"room.setAccessCode": "Код доступу до кімнати оновлений",
|
||||||
"room.accessCodeOn": "Код доступу до кімнати зараз активований",
|
"room.accessCodeOn": "Код доступу до кімнати зараз активований",
|
||||||
"room.accessCodeOff": "Код доступу до кімнати зараз відключений",
|
"room.accessCodeOff": "Код доступу до кімнати зараз відключений",
|
||||||
"room.peerChangedDisplayName": "{oldDisplayName} змінив ім'я на {displayName}",
|
"room.peerChangedDisplayName": "{oldDisplayName} змінив ім'я на {displayName}",
|
||||||
"room.newPeer": "{displayName} приєднався до кімнати",
|
"room.newPeer": "{displayName} приєднався до кімнати",
|
||||||
"room.newFile": "Новий файл є у доступі",
|
"room.newFile": "Новий файл є у доступі",
|
||||||
"room.toggleAdvancedMode": "Увімкнено розширений режим",
|
"room.toggleAdvancedMode": "Увімкнено розширений режим",
|
||||||
"room.setDemocratView": "Змінено макет на демократичний вигляд",
|
"room.setDemocratView": "Змінено макет на демократичний вигляд",
|
||||||
"room.setFilmStripView": "Змінено макет на вид фільму",
|
"room.setFilmStripView": "Змінено макет на вид фільму",
|
||||||
"room.loggedIn": "Ви ввійшли в систему",
|
"room.loggedIn": "Ви ввійшли в систему",
|
||||||
"room.loggedOut": "Ви вийшли з системи",
|
"room.loggedOut": "Ви вийшли з системи",
|
||||||
"room.changedDisplayName": "Відображуване ім’я змінено на {displayName}",
|
"room.changedDisplayName": "Відображуване ім’я змінено на {displayName}",
|
||||||
"room.changeDisplayNameError": "Сталася помилка під час зміни вашого відображуваного імені",
|
"room.changeDisplayNameError": "Сталася помилка під час зміни вашого відображуваного імені",
|
||||||
"room.chatError": "Не вдається надіслати повідомлення в чаті",
|
"room.chatError": "Не вдається надіслати повідомлення в чаті",
|
||||||
"room.aboutToJoin": "Ви збираєтесь приєднатися до зустрічі",
|
"room.aboutToJoin": "Ви збираєтесь приєднатися до зустрічі",
|
||||||
"room.roomId": "Ідентифікатор кімнати: {roomName}",
|
"room.roomId": "Ідентифікатор кімнати: {roomName}",
|
||||||
"room.setYourName": "Встановіть своє ім'я для участі та виберіть, як ви хочете приєднатися:",
|
"room.setYourName": "Встановіть своє ім'я для участі та виберіть, як ви хочете приєднатися:",
|
||||||
"room.audioOnly": "Тільки аудіо",
|
"room.audioOnly": "Тільки аудіо",
|
||||||
"room.audioVideo": "Аудіо та відео",
|
"room.audioVideo": "Аудіо та відео",
|
||||||
"room.youAreReady": "Добре, ви готові",
|
"room.youAreReady": "Добре, ви готові",
|
||||||
"room.emptyRequireLogin": "Кімната порожня! Ви можете увійти, щоб розпочати зустріч або чекати, поки хост приєднається",
|
"room.emptyRequireLogin": "Кімната порожня! Ви можете увійти, щоб розпочати зустріч або чекати, поки хост приєднається",
|
||||||
"room.locketWait": "Кімната заблокована - дочекайтеся, поки хтось не впустить вас у ...",
|
"room.locketWait": "Кімната заблокована - дочекайтеся, поки хтось не впустить вас у ...",
|
||||||
"room.lobbyAdministration": "Адміністрація залу очікування",
|
"room.lobbyAdministration": "Адміністрація залу очікування",
|
||||||
"room.peersInLobby": "Учасники залу очікувань",
|
"room.peersInLobby": "Учасники залу очікувань",
|
||||||
"room.lobbyEmpty": "Наразі у залі очікувань немає нікого",
|
"room.lobbyEmpty": "Наразі у залі очікувань немає нікого",
|
||||||
"room.hiddenPeers": "{hiddenPeersCount, множина, один {учасник} інший {учасників}}",
|
"room.hiddenPeers": "{hiddenPeersCount, множина, один {учасник} інший {учасників}}",
|
||||||
"room.me": "Я",
|
"room.me": "Я",
|
||||||
"room.spotlights": "Учасники у центрі уваги",
|
"room.spotlights": "Учасники у центрі уваги",
|
||||||
"room.passive": "Пасивні учасники",
|
"room.passive": "Пасивні учасники",
|
||||||
"room.videoPaused": "Це відео призупинено",
|
"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,
|
||||||
|
|
||||||
"tooltip.login": "Увійти",
|
"me.mutedPTT": null,
|
||||||
"tooltip.logout": "Вихід",
|
|
||||||
"tooltip.admitFromLobby": "Вхід із залу очікувань",
|
|
||||||
"tooltip.lockRoom": "Заблокувати кімнату",
|
|
||||||
"tooltip.unLockRoom": "Розблокувати кімнату",
|
|
||||||
"tooltip.enterFullscreen": "Вивести повний екран",
|
|
||||||
"tooltip.leaveFullscreen": "Залишити повноекранний екран",
|
|
||||||
"tooltip.lobby": "Показати зал очікувань",
|
|
||||||
"tooltip.settings": "Показати налаштування",
|
|
||||||
"tooltip.participants": "Показати учасників",
|
|
||||||
|
|
||||||
"label.roomName": "Назва кімнати",
|
"roles.gotRole": null,
|
||||||
"label.chooseRoomButton": "Продовжити",
|
"roles.lostRole": null,
|
||||||
"label.yourName": "Ваше ім'я",
|
|
||||||
"label.newWindow": "Нове вікно",
|
|
||||||
"label.fullscreen": "Повний екран",
|
|
||||||
"label.openDrawer": "Відкрити ящик",
|
|
||||||
"label.leave": "Залишити",
|
|
||||||
"label.chatInput": "Введіть повідомлення чату ...",
|
|
||||||
"label.chat": "Чат",
|
|
||||||
"label.filesharing": "Обмін файлами",
|
|
||||||
"label.participants": "Учасники",
|
|
||||||
"label.shareFile": "Надіслати файл",
|
|
||||||
"label.fileSharingUnsupported": "Обмін файлами не підтримується",
|
|
||||||
"label.unknown": "Невідомо",
|
|
||||||
"label.democrat": "Демократичний вигляд",
|
|
||||||
"label.filmstrip": "У вигляді кінострічки",
|
|
||||||
"label.low": "Низький",
|
|
||||||
"label.medium": "Середній",
|
|
||||||
"label.high": "Високий (HD)",
|
|
||||||
"label.veryHigh": "Дуже високий (FHD)",
|
|
||||||
"label.ultra": "Ультра (UHD)",
|
|
||||||
"label.close": "Закрити",
|
|
||||||
|
|
||||||
"settings.settings": "Налаштування",
|
"tooltip.login": "Увійти",
|
||||||
"settings.camera": "Камера",
|
"tooltip.logout": "Вихід",
|
||||||
"settings.selectCamera": "Вибрати відеопристрій",
|
"tooltip.admitFromLobby": "Вхід із залу очікувань",
|
||||||
"settings.cantSelectCamera": "Неможливо вибрати відеопристрій",
|
"tooltip.lockRoom": "Заблокувати кімнату",
|
||||||
"settings.audio": "Аудіопристрій",
|
"tooltip.unLockRoom": "Розблокувати кімнату",
|
||||||
"settings.selectAudio": "Вибрати аудіопристрій",
|
"tooltip.enterFullscreen": "Вивести повний екран",
|
||||||
"settings.cantSelectAudio": "Неможливо вибрати аудіопристрій",
|
"tooltip.leaveFullscreen": "Залишити повноекранний екран",
|
||||||
"settings.resolution": "Виберіть роздільну здатність відео",
|
"tooltip.lobby": "Показати зал очікувань",
|
||||||
"settings.layout": "Розміщення кімнати",
|
"tooltip.settings": "Показати налаштування",
|
||||||
"settings.selectRoomLayout": "Вибір розташування кімнати",
|
"tooltip.participants": "Показати учасників",
|
||||||
"settings.advancedMode": "Розширений режим",
|
"tooltip.kickParticipant": null,
|
||||||
"settings.permanentTopBar": "Постійний верхній рядок",
|
"tooltip.muteParticipant": null,
|
||||||
"settings.lastn": "Кількість видимих відео",
|
"tooltip.muteParticipantVideo": null,
|
||||||
|
"tooltip.raisedHand": null,
|
||||||
|
"tooltip.muteScreenSharing": null,
|
||||||
|
"tooltip.muteParticipantAudioModerator": null,
|
||||||
|
"tooltip.muteParticipantVideoModerator": null,
|
||||||
|
"tooltip.muteScreenSharingModerator": null,
|
||||||
|
|
||||||
"filesharing.saveFileError": "Неможливо зберегти файл",
|
"label.roomName": "Назва кімнати",
|
||||||
"filesharing.startingFileShare": "Спроба поділитися файлом",
|
"label.chooseRoomButton": "Продовжити",
|
||||||
"filesharing.successfulFileShare": "Файл готовий для обміну",
|
"label.yourName": "Ваше ім'я",
|
||||||
"filesharing.unableToShare": "Неможливо поділитися файлом",
|
"label.newWindow": "Нове вікно",
|
||||||
"filesharing.error": "Виникла помилка обміну файлами",
|
"label.fullscreen": "Повний екран",
|
||||||
"filesharing.finished": "Завантаження файлу закінчено",
|
"label.openDrawer": "Відкрити ящик",
|
||||||
"filesharing.save": "Зберегти",
|
"label.leave": "Залишити",
|
||||||
"filesharing.sharedFile": "{displayName} поділився файлом",
|
"label.chatInput": "Введіть повідомлення чату ...",
|
||||||
"filesharing.download": "Завантажити",
|
"label.chat": "Чат",
|
||||||
"filesharing.missingSeeds": "Якщо цей процес триває тривалий час, може не з’явиться хтось, хто роздає цей торрент. Спробуйте попросити когось перезавантажити потрібний файл.",
|
"label.filesharing": "Обмін файлами",
|
||||||
|
"label.participants": "Учасники",
|
||||||
|
"label.shareFile": "Надіслати файл",
|
||||||
|
"label.shareGalleryFile": null,
|
||||||
|
"label.fileSharingUnsupported": "Обмін файлами не підтримується",
|
||||||
|
"label.unknown": "Невідомо",
|
||||||
|
"label.democrat": "Демократичний вигляд",
|
||||||
|
"label.filmstrip": "У вигляді кінострічки",
|
||||||
|
"label.low": "Низький",
|
||||||
|
"label.medium": "Середній",
|
||||||
|
"label.high": "Високий (HD)",
|
||||||
|
"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,
|
||||||
|
|
||||||
"devices.devicesChanged": "Ваші пристрої змінилися, налаштуйте ваші пристрої в діалоговому вікні налаштувань",
|
"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": 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,
|
||||||
|
|
||||||
"device.audioUnsupported": "Аудіо не підтримується",
|
"filesharing.saveFileError": "Неможливо зберегти файл",
|
||||||
"device.activateAudio": "Активувати звук",
|
"filesharing.startingFileShare": "Спроба поділитися файлом",
|
||||||
"device.muteAudio": "Вимкнути звук",
|
"filesharing.successfulFileShare": "Файл готовий для обміну",
|
||||||
"device.unMuteAudio": "Увімкнути звук",
|
"filesharing.unableToShare": "Неможливо поділитися файлом",
|
||||||
|
"filesharing.error": "Виникла помилка обміну файлами",
|
||||||
|
"filesharing.finished": "Завантаження файлу закінчено",
|
||||||
|
"filesharing.save": "Зберегти",
|
||||||
|
"filesharing.sharedFile": "{displayName} поділився файлом",
|
||||||
|
"filesharing.download": "Завантажити",
|
||||||
|
"filesharing.missingSeeds": "Якщо цей процес триває тривалий час, може не з’явиться хтось, хто роздає цей торрент. Спробуйте попросити когось перезавантажити потрібний файл.",
|
||||||
|
|
||||||
"device.videoUnsupported": "Відео не підтримується",
|
"devices.devicesChanged": "Ваші пристрої змінилися, налаштуйте ваші пристрої в діалоговому вікні налаштувань",
|
||||||
"device.startVideo": "Запустити відео",
|
|
||||||
"device.stopVideo": "Зупинити відео",
|
|
||||||
|
|
||||||
"device.screenSharingUnsupported": "Обмін екраном не підтримується",
|
"device.audioUnsupported": "Аудіо не підтримується",
|
||||||
"device.startScreenSharing": "Початок спільного використання екрана",
|
"device.activateAudio": "Активувати звук",
|
||||||
"device.stopScreenSharing": "Зупинити спільний доступ до екрана",
|
"device.muteAudio": "Вимкнути звук",
|
||||||
|
"device.unMuteAudio": "Увімкнути звук",
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Мікрофон відключений",
|
"device.videoUnsupported": "Відео не підтримується",
|
||||||
"devices.microphoneError": "Сталася помилка під час доступу до мікрофона",
|
"device.startVideo": "Запустити відео",
|
||||||
"devices.microPhoneMute": "Вимкнено ваш мікрофон",
|
"device.stopVideo": "Зупинити відео",
|
||||||
"devices.micophoneUnMute": "Не ввімкнено ваш мікрофон",
|
|
||||||
"devices.microphoneEnable": "Увімкнено мікрофон",
|
|
||||||
"devices.microphoneMuteError": "Не вдається вимкнути мікрофон",
|
|
||||||
"devices.microphoneUnMuteError": "Неможливо ввімкнути мікрофон",
|
|
||||||
|
|
||||||
"devices.screenSharingDisconnected": "Спільний доступ до екрана відключений",
|
"device.screenSharingUnsupported": "Обмін екраном не підтримується",
|
||||||
"devices.screenSharingError": "Сталася помилка під час доступу до екрану",
|
"device.startScreenSharing": "Початок спільного використання екрана",
|
||||||
|
"device.stopScreenSharing": "Зупинити спільний доступ до екрана",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Камера відключена",
|
"devices.microphoneDisconnected": "Мікрофон відключений",
|
||||||
"devices.cameraError": "Під час доступу до камери сталася помилка"
|
"devices.microphoneError": "Сталася помилка під час доступу до мікрофона",
|
||||||
|
"devices.microPhoneMute": "Вимкнено ваш мікрофон",
|
||||||
|
"devices.micophoneUnMute": "Не ввімкнено ваш мікрофон",
|
||||||
|
"devices.microphoneEnable": "Увімкнено мікрофон",
|
||||||
|
"devices.microphoneMuteError": "Не вдається вимкнути мікрофон",
|
||||||
|
"devices.microphoneUnMuteError": "Неможливо ввімкнути мікрофон",
|
||||||
|
|
||||||
|
"devices.screenSharingDisconnected": "Спільний доступ до екрана відключений",
|
||||||
|
"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
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
export function getSignalingUrl(peerId, roomId)
|
export function getSignalingUrl(peerId, roomId)
|
||||||
{
|
{
|
||||||
const hostname = window.config.multipartyServer;
|
|
||||||
|
|
||||||
const port =
|
const port =
|
||||||
process.env.NODE_ENV !== 'production' ?
|
process.env.NODE_ENV !== 'production' ?
|
||||||
window.config.developmentPort
|
window.config.developmentPort
|
||||||
:
|
:
|
||||||
window.config.productionPort;
|
window.config.productionPort;
|
||||||
|
|
||||||
const url = `wss://${hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
|
const url = `wss://${window.location.hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue