From 0d15c01077f6b09f602686cb971929d71751102e Mon Sep 17 00:00:00 2001 From: Dan Dascalescu Date: Tue, 30 Jun 2020 18:57:25 +1200 Subject: [PATCH 001/167] doc: TOOLBAR_BUTTONS clarifications --- interface_config.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/interface_config.js b/interface_config.js index 199ab4f97..b686099cb 100644 --- a/interface_config.js +++ b/interface_config.js @@ -174,11 +174,15 @@ var interfaceConfig = { TOOLBAR_ALWAYS_VISIBLE: false, /** - * The name of the toolbar buttons to display in the toolbar. If present, - * the button will display. Exceptions are "livestreaming" and "recording" - * which also require being a moderator and some values in config.js to be - * enabled. Also, the "profile" button will not display for users with a - * jwt. + * The name of the toolbar buttons to display in the toolbar, including the + * "More actions" menu. If present, the button will display. Exceptions are + * "livestreaming" and "recording" which also require being a moderator and + * some values in config.js to be enabled. Also, the "profile" button will + * not display for users with a JWT. + * Notes: + * - it's impossible to choose which buttons go in the "More actions" menu + * - it's impossible to control the placement of buttons + * - 'desktop' controls the "Share your screen" button */ TOOLBAR_BUTTONS: [ 'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen', From 0e1ecd3256cb7d60563fac1c81f45a1437a52fe4 Mon Sep 17 00:00:00 2001 From: Jaya Allamsetty Date: Mon, 29 Jun 2020 16:59:28 -0400 Subject: [PATCH 002/167] fix: disable audio/video settings popup on mobile browsers Mobile devices do not support capture from multiple cameras/mics at a time. --- .../toolbox/components/web/AudioSettingsButton.js | 7 +++++-- .../toolbox/components/web/VideoSettingsButton.js | 9 +++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/react/features/toolbox/components/web/AudioSettingsButton.js b/react/features/toolbox/components/web/AudioSettingsButton.js index 207fa2b03..63acf8cbd 100644 --- a/react/features/toolbox/components/web/AudioSettingsButton.js +++ b/react/features/toolbox/components/web/AudioSettingsButton.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; +import { isMobileBrowser } from '../../../base/environment/utils'; import { IconArrowDown } from '../../../base/icons'; import JitsiMeetJS from '../../../base/lib-jitsi-meet/_'; import { connect } from '../../../base/redux'; @@ -31,6 +32,7 @@ type Props = { /** * Flag controlling the visibility of the button. + * AudioSettings popup is disabled on mobile browsers. */ visible: boolean, }; @@ -130,7 +132,7 @@ class AudioSettingsButton extends Component { - ) : null; + ) : ; } } @@ -143,7 +145,8 @@ class AudioSettingsButton extends Component { function mapStateToProps(state) { return { isDisabled: isAudioSettingsButtonDisabled(state), - permissionPromptVisibility: getMediaPermissionPromptVisibility(state) + permissionPromptVisibility: getMediaPermissionPromptVisibility(state), + visible: !isMobileBrowser() }; } diff --git a/react/features/toolbox/components/web/VideoSettingsButton.js b/react/features/toolbox/components/web/VideoSettingsButton.js index 8e3a98f0f..31693e731 100644 --- a/react/features/toolbox/components/web/VideoSettingsButton.js +++ b/react/features/toolbox/components/web/VideoSettingsButton.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; +import { isMobileBrowser } from '../../../base/environment/utils'; import { IconArrowDown } from '../../../base/icons'; import JitsiMeetJS from '../../../base/lib-jitsi-meet/_'; import { connect } from '../../../base/redux'; @@ -37,6 +38,9 @@ type Props = { /** * Flag controlling the visibility of the button. + * VideoSettings popup is currently disabled on mobile browsers + * as mobile devices do not support capture of more than one + * camera at a time. */ visible: boolean, }; @@ -144,7 +148,7 @@ class VideoSettingsButton extends Component { - ) : null; + ) : ; } } @@ -158,7 +162,8 @@ function mapStateToProps(state) { return { hasVideoTrack: Boolean(getLocalJitsiVideoTrack(state)), isDisabled: isVideoSettingsButtonDisabled(state), - permissionPromptVisibility: getMediaPermissionPromptVisibility(state) + permissionPromptVisibility: getMediaPermissionPromptVisibility(state), + visible: !isMobileBrowser() }; } From 79231914b9fd59d2852851d2ac67d4adbf1f18b9 Mon Sep 17 00:00:00 2001 From: Dan Dascalescu Date: Sat, 27 Jun 2020 15:14:32 -0700 Subject: [PATCH 003/167] docs: fix typo in interface_config.js --- interface_config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface_config.js b/interface_config.js index b686099cb..dfcc6cdfc 100644 --- a/interface_config.js +++ b/interface_config.js @@ -114,7 +114,7 @@ var interfaceConfig = { LOCAL_THUMBNAIL_RATIO: 16 / 9, // 16:9 /** - * Maximum coeficient of the ratio of the large video to the visible area + * Maximum coefficient of the ratio of the large video to the visible area * after the large video is scaled to fit the window. * * @type {number} From ea07515138f313f882ad4cfadb447a0930e457e2 Mon Sep 17 00:00:00 2001 From: Dan Dascalescu Date: Sat, 27 Jun 2020 09:43:22 -0700 Subject: [PATCH 004/167] docs: improve English in config.js --- config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.js b/config.js index 4cfbde296..f5a14bc9d 100644 --- a/config.js +++ b/config.js @@ -303,7 +303,7 @@ var config = { // and microsoftApiApplicationClientID // enableCalendarIntegration: false, - // When 'true', it shows an intermediate page before joining, where the user can configure its devices. + // When 'true', it shows an intermediate page before joining, where the user can configure their devices. // prejoinPageEnabled: false, // If true, shows the unsafe roon name warning label when a room name is @@ -328,10 +328,10 @@ var config = { // callStatsID: '', // callStatsSecret: '', - // enables sending participants display name to callstats + // Enables sending participants' display names to callstats // enableDisplayNameInStats: false, - // enables sending participants email if available to callstats and other analytics + // Enables sending participants' emails (if available) to callstats and other analytics // enableEmailInStats: false, // Privacy From b4ecef429ada7181c1ac3f530c49ba43da2f6e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 23 Jun 2020 15:32:25 +0200 Subject: [PATCH 005/167] doc: add H1 to SECURITY.md --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index d8441922d..58d25b8ac 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,9 +1,9 @@ # Security -## Reporting security issuess +## Reporting security issues We take security very seriously and develop all Jitsi projects to be secure and safe. -If you find (or simply suspect) a security issue in any of the Jitsi projects, please send us an email to security@jitsi.org. +If you find (or simply suspect) a security issue in any of the Jitsi projects, please report it to us via [HackerOne](https://hackerone.com/8x8) or send us an email to security@jitsi.org. **We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.** From 346dac476ae4e764349dc7bd4fd29a522159cc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 30 Jun 2020 09:59:02 +0200 Subject: [PATCH 006/167] deps: clean-css@4.3.0 --- package-lock.json | 40 ++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index decb607cc..bab0360a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6221,33 +6221,31 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" }, - "clean-css": { - "version": "3.4.25", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.25.tgz", - "integrity": "sha1-nppS1cHmvFEj4bJ4P6Zf6ViUbt4=", + "clean-css-cli": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.3.0.tgz", + "integrity": "sha512-8GHZfr+mG3zB/Lgqrr27qHBFsPSn0fyEI3f2rIZpxPxUbn2J6A8xyyeBRVTW8duDuXigN0s80vsXiXJOEFIO5Q==", "dev": true, "requires": { - "commander": "2.8.x", - "source-map": "0.4.x" + "clean-css": "^4.2.1", + "commander": "2.x", + "glob": "7.x" }, "dependencies": { - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", "dev": true, "requires": { - "graceful-readlink": ">= 1.0.0" + "source-map": "~0.6.0" } }, "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -9429,12 +9427,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", diff --git a/package.json b/package.json index bd912914d..614b07e39 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "babel-eslint": "10.0.1", "babel-loader": "8.0.4", "circular-dependency-plugin": "5.2.0", - "clean-css": "3.4.25", + "clean-css-cli": "4.3.0", "css-loader": "0.28.7", "eslint": "5.6.1", "eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#1.0.3", From 7d18183bf9925f8c362eacf1970c472902ea0a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 30 Jun 2020 10:02:55 +0200 Subject: [PATCH 007/167] deps: css-loader@3.6.0 --- package-lock.json | 1269 +++++++-------------------------------------- package.json | 2 +- 2 files changed, 200 insertions(+), 1071 deletions(-) diff --git a/package-lock.json b/package-lock.json index bab0360a5..a43529acc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4119,6 +4119,12 @@ "@types/istanbul-lib-report": "*" } }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -4444,12 +4450,6 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -5171,32 +5171,6 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - } - } - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -5209,17 +5183,6 @@ "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, "babel-eslint": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", @@ -5977,36 +5940,6 @@ } } }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "^1.3.6", - "caniuse-db": "^1.0.30000529", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - } - } - }, - "caniuse-db": { - "version": "1.0.30000810", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000810.tgz", - "integrity": "sha1-vSWDDEHvq2Qzmi44H0lnc0PIRQk=", - "dev": true - }, "caniuse-lite": { "version": "1.0.30000989", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz", @@ -6181,15 +6114,6 @@ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "^1.1.3" - } - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -6320,15 +6244,6 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "^1.1.2" - } - }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -6343,17 +6258,6 @@ "object-visit": "^1.0.0" } }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", @@ -6367,15 +6271,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "^1.0.0" - } - }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -6386,25 +6281,6 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==" }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "^0.11.0", - "css-color-names": "0.0.4", - "has": "^1.0.1" - }, - "dependencies": { - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - } - } - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6869,48 +6745,116 @@ "integrity": "sha1-U12AiRs68hOV0AXxmQX+UVS1zP8=" }, "css-loader": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz", - "integrity": "sha512-GxMpax8a/VgcfRrVy0gXD6yLd5ePYbXX/5zGgTVYp4wXtJklS8Z2VaUArJgc//f6/Dzil7BaJObdSv8eKKCPgg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", "dev": true, "requires": { - "babel-code-frame": "^6.11.0", - "css-selector-tokenizer": "^0.7.0", - "cssnano": ">=2.6.1 <4", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", - "lodash.camelcase": "^4.3.0", - "object-assign": "^4.0.1", - "postcss": "^5.0.6", - "postcss-modules-extract-imports": "^1.0.0", - "postcss-modules-local-by-default": "^1.0.1", - "postcss-modules-scope": "^1.0.0", - "postcss-modules-values": "^1.1.0", - "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" }, "dependencies": { - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } + }, + "ajv-keywords": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.0.tgz", + "integrity": "sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -6920,61 +6864,11 @@ "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==" }, "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "^6.3.1", - "decamelize": "^1.1.2", - "defined": "^1.0.0", - "has": "^1.0.1", - "object-assign": "^4.0.1", - "postcss": "^5.0.14", - "postcss-calc": "^5.2.0", - "postcss-colormin": "^2.1.8", - "postcss-convert-values": "^2.3.4", - "postcss-discard-comments": "^2.0.4", - "postcss-discard-duplicates": "^2.0.1", - "postcss-discard-empty": "^2.0.1", - "postcss-discard-overridden": "^0.1.1", - "postcss-discard-unused": "^2.2.1", - "postcss-filter-plugins": "^2.0.0", - "postcss-merge-idents": "^2.1.5", - "postcss-merge-longhand": "^2.0.1", - "postcss-merge-rules": "^2.0.3", - "postcss-minify-font-values": "^1.0.2", - "postcss-minify-gradients": "^1.0.1", - "postcss-minify-params": "^1.0.4", - "postcss-minify-selectors": "^2.0.4", - "postcss-normalize-charset": "^1.1.0", - "postcss-normalize-url": "^3.0.7", - "postcss-ordered-values": "^2.1.0", - "postcss-reduce-idents": "^2.2.2", - "postcss-reduce-initial": "^1.0.0", - "postcss-reduce-transforms": "^1.0.3", - "postcss-svgo": "^2.1.1", - "postcss-unique-selectors": "^2.0.2", - "postcss-value-parser": "^3.2.3", - "postcss-zindex": "^2.0.1" - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "^1.0.9", - "source-map": "^0.5.3" - } - }, "csstype": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz", @@ -7139,12 +7033,6 @@ } } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, "del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -7356,12 +7244,6 @@ "integrity": "sha512-kS/gEPzZs3Y1rRsbGX4UOSjtP/CeJP0CxSNZHYxGfVM/VgLcv0ZqM7C45YyTj2DI2g7+P9Dd24C+IMIg6D0nYQ==", "dev": true }, - "electron-to-chromium": { - "version": "1.3.34", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.34.tgz", - "integrity": "sha1-2TSY9AORuwwWpgPYJBuZUUBBV+0=", - "dev": true - }, "elliptic": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", @@ -8001,12 +7883,6 @@ } } }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", @@ -8358,12 +8234,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -8589,12 +8459,6 @@ "write": "^0.2.1" } }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, "flow-bin": { "version": "0.104.0", "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.104.0.tgz", @@ -9692,12 +9556,6 @@ "wbuf": "^1.1.0" } }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, "html-entities": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", @@ -9839,73 +9697,13 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", "dev": true, "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", - "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", - "dev": true, - "requires": { - "chalk": "^2.3.1", - "source-map": "^0.6.1", - "supports-color": "^5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "postcss": "^7.0.14" } }, "ieee754": { @@ -10202,12 +10000,6 @@ "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", "dev": true }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -10365,12 +10157,6 @@ "path-is-inside": "^1.0.1" } }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -10403,15 +10189,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, "is-symbol": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", @@ -10777,16 +10554,6 @@ } } }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" - } - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -11028,12 +10795,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==" }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -11054,23 +10815,11 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, "lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -11155,12 +10904,6 @@ "yallist": "^2.1.2" } }, - "macaddress": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.9.tgz", - "integrity": "sha512-k4F1JUof6cQXxNFzx3thLby4oJzXTXQueAOOts944Vqizn+Rjc2QNFenT9FJSLU1CH3PmrHRSyZs2E+Cqw+P2w==", - "dev": true - }, "make-dir": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", @@ -11213,12 +10956,6 @@ "object-visit": "^1.0.0" } }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -12594,24 +12331,6 @@ "remove-trailing-separator": "^1.0.1" } }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -12645,12 +12364,6 @@ "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -13542,567 +13255,135 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", "dev": true, "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "^5.0.2", - "postcss-message-helpers": "^2.0.0", - "reduce-css-calc": "^1.2.6" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "^1.0.5", - "postcss": "^5.0.13", - "postcss-value-parser": "^3.2.3" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "^5.0.11", - "postcss-value-parser": "^3.1.2" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "^5.0.14" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "^5.0.14" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "^5.0.16" - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "^5.0.14", - "uniqs": "^2.0.0" - } - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "uniqid": "^4.0.0" - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.10", - "postcss-value-parser": "^3.1.1" - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "^1.5.2", - "caniuse-api": "^1.5.2", - "postcss": "^5.0.4", - "postcss-selector-parser": "^2.2.2", - "vendors": "^1.0.0" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" }, "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "^5.0.12", - "postcss-value-parser": "^3.3.0" - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.2", - "postcss-value-parser": "^3.0.2", - "uniqs": "^2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.2", - "has": "^1.0.1", - "postcss": "^5.0.14", - "postcss-selector-parser": "^2.0.0" - } - }, "postcss-modules-extract-imports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", - "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", "dev": true, "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", - "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", - "dev": true, - "requires": { - "chalk": "^2.3.1", - "source-map": "^0.6.1", - "supports-color": "^5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "postcss": "^7.0.5" } }, "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", "dev": true, "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true - }, - "postcss": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", - "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", - "dev": true, - "requires": { - "chalk": "^2.3.1", - "source-map": "^0.6.1", - "supports-color": "^5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", "dev": true, "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", - "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", - "dev": true, - "requires": { - "chalk": "^2.3.1", - "source-map": "^0.6.1", - "supports-color": "^5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" } }, "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", "dev": true, "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", - "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", - "dev": true, - "requires": { - "chalk": "^2.3.1", - "source-map": "^0.6.1", - "supports-color": "^5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "^5.0.5" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^1.4.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.1" - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.8", - "postcss-value-parser": "^3.0.1" + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" } }, "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", "dev": true, "requires": { - "flatten": "^1.0.2", + "cssesc": "^3.0.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "^2.0.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3", - "svgo": "^0.7.0" - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" - } - }, "postcss-value-parser": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" - } - }, "postis": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/postis/-/postis-2.2.0.tgz", @@ -14114,12 +13395,6 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, "pretty-format": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", @@ -14301,16 +13576,6 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -15290,42 +14555,6 @@ "strip-indent": "^1.0.1" } }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2", - "math-expression-evaluator": "^1.2.14", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, "redux": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz", @@ -15350,12 +14579,6 @@ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz", "integrity": "sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU=" }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, "regenerate-unicode-properties": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz", @@ -15557,29 +14780,6 @@ } } }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -16496,15 +15696,6 @@ } } }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", @@ -16787,12 +15978,6 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, "string-replace-loader": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-2.1.1.tgz", @@ -17386,35 +16571,6 @@ "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.2.tgz", "integrity": "sha512-1gtApepKFweigFZj3sGO8KT8LvVZK8io146EzXrpVuWCDAbISz/yMucco3hWTkpZNoPabM+dnMOpy6Swue68Zg==" }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "~1.0.1", - "colors": "~1.1.2", - "csso": "~2.3.1", - "js-yaml": "~3.7.0", - "mkdirp": "~0.5.1", - "sax": "~1.2.1", - "whet.extend": "~0.9.9" - }, - "dependencies": { - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - } - } - }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -18072,21 +17228,6 @@ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "^0.2.8" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -18286,12 +17427,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", - "dev": true - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -19768,12 +18903,6 @@ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", diff --git a/package.json b/package.json index 614b07e39..269bdaf21 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "babel-loader": "8.0.4", "circular-dependency-plugin": "5.2.0", "clean-css-cli": "4.3.0", - "css-loader": "0.28.7", + "css-loader": "3.6.0", "eslint": "5.6.1", "eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#1.0.3", "eslint-plugin-flowtype": "2.50.3", From 7d620207874e447ab4bc130cfdf9ab80ddf0712a Mon Sep 17 00:00:00 2001 From: Bettenbuk Zoltan Date: Mon, 29 Jun 2020 14:17:35 +0200 Subject: [PATCH 008/167] feat: add moderated service link to welcome page --- config.js | 5 +++++ css/_welcome_page.scss | 16 ++++++++++++++++ lang/main.json | 5 +++-- .../welcome/components/AbstractWelcomePage.js | 12 +++++++----- .../welcome/components/WelcomePage.web.js | 14 ++++++++++++-- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/config.js b/config.js index f5a14bc9d..8841d9ec1 100644 --- a/config.js +++ b/config.js @@ -518,6 +518,11 @@ var config = { */ // brandingDataUrl: '', + // The URL of the moderated rooms microservice, if available. If it + // is present, a link to the service will be rendered on the welcome page, + // otherwise the app doesn't render it. + // moderatedRoomServiceUrl: 'https://moderated.jitsi-meet.example.com', + // List of undocumented settings used in jitsi-meet /** _immediateReloadThreshold diff --git a/css/_welcome_page.scss b/css/_welcome_page.scss index 2a5145719..211da17e2 100644 --- a/css/_welcome_page.scss +++ b/css/_welcome_page.scss @@ -111,6 +111,22 @@ body.welcome-page { } + #moderated-meetings { + max-width: calc(100% - 40px); + padding: 16px 0 39px 0; + width: $welcomePageEnterRoomWidth; + + p { + color: $welcomePageDescriptionColor; + text-align: left; + + a { + color: inherit; + font-weight: 600; + } + } + } + .tab-container { font-size: 16px; position: relative; diff --git a/lang/main.json b/lang/main.json index 026f00fe1..a6183abf3 100644 --- a/lang/main.json +++ b/lang/main.json @@ -839,16 +839,17 @@ "connectCalendarText": "Connect your calendar to view all your meetings in {{app}}. Plus, add {{provider}} meetings to your calendar and start them with one click.", "enterRoomTitle": "Start a new meeting", "getHelp": "Get help", - "roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.", "go": "GO", "goSmall": "GO", - "join": "CREATE / JOIN", "info": "Info", + "join": "CREATE / JOIN", + "moderatedMessage": "Or book a meeting URL in advance where you are the only moderator.", "privacy": "Privacy", "recentList": "Recent", "recentListDelete": "Delete", "recentListEmpty": "Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.", "reducedUIText": "Welcome to {{app}}!", + "roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.", "roomname": "Enter room name", "roomnameHint": "Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.", "sendFeedback": "Send feedback", diff --git a/react/features/welcome/components/AbstractWelcomePage.js b/react/features/welcome/components/AbstractWelcomePage.js index b52a3525f..7affe0dd1 100644 --- a/react/features/welcome/components/AbstractWelcomePage.js +++ b/react/features/welcome/components/AbstractWelcomePage.js @@ -25,6 +25,11 @@ type Props = { */ _enableInsecureRoomNameWarning: boolean, + /** + * URL for the moderated rooms microservice, if available. + */ + _moderatedRoomServiceUrl: ?string, + /** * Whether the recent list is enabled */ @@ -269,16 +274,13 @@ export class AbstractWelcomePage extends Component { * * @param {Object} state - The redux state. * @protected - * @returns {{ - * _calendarEnabled: boolean, - * _room: string, - * _settings: Object - * }} + * @returns {Props} */ export function _mapStateToProps(state: Object) { return { _calendarEnabled: isCalendarEnabled(state), _enableInsecureRoomNameWarning: state['features/base/config'].enableInsecureRoomNameWarning || false, + _moderatedRoomServiceUrl: state['features/base/config'].moderatedRoomServiceUrl, _recentListEnabled: isRecentListEnabled(), _room: state['features/base/conference'].room, _settings: state['features/base/settings'] diff --git a/react/features/welcome/components/WelcomePage.web.js b/react/features/welcome/components/WelcomePage.web.js index f8db57e70..5fb77307c 100644 --- a/react/features/welcome/components/WelcomePage.web.js +++ b/react/features/welcome/components/WelcomePage.web.js @@ -3,7 +3,7 @@ import React from 'react'; import { isMobileBrowser } from '../../base/environment/utils'; -import { translate } from '../../base/i18n'; +import { translate, translateToHTML } from '../../base/i18n'; import { Icon, IconWarning } from '../../base/icons'; import { Watermarks } from '../../base/react'; import { connect } from '../../base/redux'; @@ -158,7 +158,7 @@ class WelcomePage extends AbstractWelcomePage { * @returns {ReactElement|null} */ render() { - const { t } = this.props; + const { _moderatedRoomServiceUrl, t } = this.props; const { APP_NAME } = interfaceConfig; const showAdditionalContent = this._shouldShowAdditionalContent(); const showAdditionalToolbarContent = this._shouldShowAdditionalToolbarContent(); @@ -224,6 +224,16 @@ class WelcomePage extends AbstractWelcomePage { } + { _moderatedRoomServiceUrl && ( +
+

+ { + translateToHTML( + t, 'welcomepage.moderatedMessage', { url: _moderatedRoomServiceUrl }) + } +

+
+ ) } { this._renderTabs() } { showAdditionalContent From eac891585b2a7a29f047cf84ec307bd2a157f039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= <65037655+Quenty-tolosan@users.noreply.github.com> Date: Tue, 30 Jun 2020 15:02:16 +0200 Subject: [PATCH 009/167] lang: update Occitan --- lang/languages-oc.json | 8 ++- lang/main-oc.json | 114 +++++++++++++++++++++++++++++++++-------- 2 files changed, 99 insertions(+), 23 deletions(-) diff --git a/lang/languages-oc.json b/lang/languages-oc.json index 0e42f56af..fa3e8a8c5 100644 --- a/lang/languages-oc.json +++ b/lang/languages-oc.json @@ -38,5 +38,11 @@ "lt": "Lituanian", "id": "Indonesian", "he": "Ebrèu", - "eu": "Basc" + "eu": "Basc", + "mr": "Marathi", + "sl": "Eslovèn", + "ro": "Romanian", + "ar": "Arabi" } + + diff --git a/lang/main-oc.json b/lang/main-oc.json index 49f283587..07dfafacc 100644 --- a/lang/main-oc.json +++ b/lang/main-oc.json @@ -15,7 +15,22 @@ "searchPeople": "Cercar de monde", "searchPeopleAndNumbers": "Cercar de monde o apondre lor numèros de telefòn", "telephone": "Telefòn : {{number}}", - "title": "Convidatz de monde a vòstra conferéncia" + "title": "Convidatz de monde a vòstra conferéncia", + "shareStream": "Partejar la ligam de la difusion en dirècte", + "copyStream": "Copiar lo ligam de la difusion en dirècte", + "yahooEmail": "Yahoo Email", + "outlookEmail": "Outlook Email", + "shareLink": "Partejar lo ligam de la conferéncia per convidar de monde", + "shareInvite": "Partejar invitacion conferéncia", + "linkCopied": "Ligam copiat al quichapapièrs", + "inviteMorePrompt": "Convidar mai de monde", + "inviteMoreMailSubject": "Rejónher la conferéncia {{appName}}", + "inviteMoreHeader": "Sètz l’unica persona de la conferéncia", + "googleEmail": "Google Email", + "defaultEmail": "Vòstre email per defaut", + "copyLink": "Copiar lo ligam de la conferéncia", + "copyInvite": "Copiar l’invitacion a la conferéncia", + "addContacts": "Convidatz vòstres contactes" }, "audioDevices": { "bluetooth": "Bluetooth", @@ -122,7 +137,10 @@ "launchWebButton": "Lançar del navigador", "openApp": "Telecargar l’aplicacion", "title": "Aviada de vòstra conferéncia dins {{app}}…", - "tryAgainButton": "Tornar ensajar del burèu" + "tryAgainButton": "Tornar ensajar del burèu", + "joinInApp": "Rejónher la conferéncia en utilizant l’aplicacion", + "ifHaveApp": "S’avètz ja l’aplicacion :", + "ifDoNotHaveApp": "S’avètz pas encara l’aplicacion :" }, "defaultLink": "ex. {{url}}", "defaultNickname": "ex. Joan Delpuèch", @@ -238,7 +256,7 @@ "screenSharingFailedToInstallTitle": "Fracàs de l'installacion de partatge d'ecran", "screenSharingFirefoxPermissionDeniedError": "Quicòm a fach mèuca quand èrem a ensajar de partejar vòstre ecran. Mercés de verificar qu’avètz donat l’autorizacion de lo partejar.", "screenSharingFirefoxPermissionDeniedTitle": "Ops ! Avèm pas pogut aviar lo partatge d’ecran.", - "screenSharingPermissionDeniedError": "Òups ! Quicòm s'es pas ben passat amb l'autorizacion de vòstra extension de partatge d'ecran. Mercés de recargar e tornar ensajar.", + "screenSharingPermissionDeniedError": "Òps ! Quicòm s'es pas ben passat amb l'autorizacion de vòstra autorizacion de partatge d'ecran. Mercés de recargar e tornar ensajar.", "sendPrivateMessage": "Avètz recentament recebut un messatge privat. Avètz ensajat d’i respondre en privat, o volètz enviar lo messatge al grop ?", "sendPrivateMessageCancel": "Enviar al grop", "sendPrivateMessageOk": "Enviar en privat", @@ -257,7 +275,7 @@ "stopLiveStreaming": "Arrestar lo dirècte", "stopRecording": "Arrestar l'enregistrament", "stopRecordingWarning": "Sètz segur que volètz arrestar l'enregistrament?", - "stopStreamingWarning": "Sètz segur que volètz arrestar lo dirècte?", + "stopStreamingWarning": "Volètz vertadièrament arrestar lo dirècte ?", "streamKey": "Clau del dirècte", "Submit": "Validar", "thankYou": "Mercé d'aver utilizat {{appName}} !", @@ -273,12 +291,11 @@ "Yes": "Òc", "yourEntireScreen": "Vòstre ecran complet", "screenSharingAudio": "Partejar l’àudio", - "muteEveryoneStartMuted": "", - "muteEveryoneSelf": "", - "muteEveryoneTitle": "", - "muteEveryoneDialog": "", - "muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}} ?", - "muteEveryoneElseDialog": "" + "muteEveryoneStartMuted": "D'ara enlà tot lo monde comença mut", + "muteEveryoneSelf": "vos", + "muteEveryoneTitle": "Rendre mut tot lo monde ?", + "muteEveryoneDialog": "Volètz vertadièrament copar lo son a tot lo monde ? Poiretz pas lo restablir, mas eles poiràn o far quora que vòlgan.", + "muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}} ?" }, "dialOut": { "statusMessage": "ara es {{status}}" @@ -381,7 +398,7 @@ "invalidStreamKey": "La clau de difusion en dirècte es benlèu pas corrècta.", "off": "La difusion en dirècte es estada arrestada", "offBy": "{{name}} a arrestat la difusion en dirècte", - "on": "La difusion en dirècte es estada arrestada", + "on": "Difusion en dirècte", "onBy": "{{name}} a començat la difusion en dirècte", "pending": "Començar lo dirècte…", "serviceName": "Servici de difusion en dirècte", @@ -458,7 +475,9 @@ "newDeviceAudioTitle": "Nòu periferic àudio detectat", "newDeviceAction": "Utilizar", "oldElectronClientDescription3": " ara !", - "oldElectronClientDescription2": "darrièra compilacion" + "oldElectronClientDescription2": "darrièra compilacion", + "oldElectronClientDescription1": "Sembla qu’utilizatz una version anciana del client Jitsi Meet qu’es conegut per conténer de problèmas de seguretat. Mercés de vos assegurar de metre a jorn ", + "OldElectronAPPTitle": "Problèma de seguretat !" }, "passwordSetRemotely": "causit per qualqu'un mai", "passwordDigitsOnly": "Fins a {{number}} chifras", @@ -557,7 +576,9 @@ "startWithAudioMuted": "Començar sens son", "startWithVideoMuted": "Començar sens vièdo", "version": "Version", - "alertCancel": "Anullar" + "alertCancel": "Anullar", + "disableCrashReportingWarning": "Volètz vertadièrament desactivar lo rapòrt de plantatge ? Lo paramètre serà aplicat aprèp la reaviada de l’aplicacion.", + "disableCrashReporting": "Desactivar lo rapòrt de plantatge" }, "share": { "dialInfoText": "\n\n=====\n\nVolètz sonar de vòstre telefòn estant ?\n\n{{defaultDialInNumber}}Clicatz lo ligam per veire los numèros de telefòn d’aquesta conferéncia\n{{dialInfoPageUrl}}", @@ -618,9 +639,10 @@ "toggleCamera": "Passar a la camèra", "videomute": "Silenciar la vidèo", "videoblur": "Enfoscar o non la vidèo", - "muteEveryone": "", + "muteEveryone": "Rendre mut tot lo monde", "moreOptions": "Mostrar mai d’opcions", - "e2ee": "Chiframent del cap a la fin" + "e2ee": "Chiframent del cap a la fin", + "security": "Opcions de seguretat" }, "addPeople": "Ajustar de monde a vòstra sonada", "audioOnlyOff": "Desactivar lo mòde connexion febla", @@ -672,12 +694,13 @@ "startvideoblur": "Trebolar mon rèire-plan", "stopvideoblur": "Desactivar lo borrolatge del rèire-plan", "noisyAudioInputDesc": "", - "noisyAudioInputTitle": "", + "noisyAudioInputTitle": "Vòstre microfòn sembla brusent !", "noAudioSignalDialInLinkDesc": "", "noAudioSignalDialInDesc": "", - "muteEveryone": "", + "muteEveryone": "Rendre mut tot lo monde", "moreOptions": "Autras opcions", - "e2ee": "Chiframent del cap a la fin" + "e2ee": "Chiframent del cap a la fin", + "security": "Opcions de seguretat" }, "transcribing": { "ccButtonTooltip": "Aviar / Arrestat los sostítols", @@ -741,7 +764,7 @@ "remoteControl": "Contraròtle alonhat", "show": "Mostrar davant", "videomute": "Lo participant a arrestat la camèra", - "domuteOthers": "" + "domuteOthers": "Rendre mut totes los autres" }, "welcomepage": { "accessibilityLabel": { @@ -778,8 +801,8 @@ "header": "Centre d’ajuda" }, "lonelyMeetingExperience": { - "youAreAlone": "", - "button": "" + "youAreAlone": "Sètz l’unica persona de la conferéncia", + "button": "Convidar d’autres" }, "chromeExtensionBanner": { "dontShowAgain": "Me mostrar pas mai aquò", @@ -796,6 +819,53 @@ "callMe": "Sona-me", "audioTrackError": "Creacion impossibla de la pista àudio.", "audioOnlyError": "Error àudio :", - "audioAndVideoError": "Error àudio e vidèo :" + "audioAndVideoError": "Error àudio e vidèo :", + "dialing": "A sonar", + "viewAllNumbers": "veire totes los numèros", + "videoTrackError": "Creacion d’una pista àudio impossibla.", + "videoOnlyError": "Error vidèo :", + "screenSharingError": "Error de partatge d’ecran :", + "startWithPhone": "Començar amb l’àudio del telefòn", + "calling": "A sonar", + "lookGood": "Sembla que lo microfòn fonciona pas coma cal", + "linkCopied": "Ligam copiat al quichapapièrs", + "initiated": "Sonada aviada", + "joinWithoutAudio": "Rejónher sens àudio", + "joinMeeting": "Rejónher la conferéncia", + "joinAudioByPhone": "Rejónher amb l’àudio del telefòn" + }, + "lobby": { + "reject": "Regetar", + "passwordJoinButton": "Rejónher", + "passwordField": "Picatz lo senhal de la conferéncia", + "nameField": "Escrivètz vòstre nom", + "knockTitle": "Qualqu’un vòl rejónher la conferéncia", + "knockButton": "Demandar a rejónher", + "joiningWithPasswordTitle": "A rejónher amb senhal...", + "joiningTitle": "Demanda a rejónher la conferéncia...", + "joinTitle": "Rejónher la conferéncia", + "joinRejectedMessage": "Un moderator a regetat vòstra demanda de participacion.", + "joinWithPasswordMessage": "Ensag de participacion amb senhal, volgatz esperar...", + "joiningMessage": "Dintraretz dins la conferéncia tre que qualqu’un aurà acceptat vòstra demanda", + "invalidPassword": "Senhal invalid", + "enterPasswordTitle": "Dintratz lo senhal per rejónher la conferéncia", + "enterPasswordButton": "Dintratz lo senhal de la conferéncia", + "enableDialogSubmit": "Activar", + "enableDialogPasswordField": "Definir un senhal (opcional)", + "emailField": "Picata vòstra adreça electronica", + "disableDialogSubmit": "Desactivar", + "backToKnockModeButton": "Cap de senhal, demandar a participar a la plaça", + "allow": "Autorizar" + }, + "security": { + "securityOptions": "Opcions de seguretat", + "insecureRoomNameWarning": "Lo nom de la sala es pas segur. De monde indesirables poirián rejónher vòstra conferéncia.", + "aboutReadOnly": "Los participants que son moderators pòdon ajustar un $t(lockRoomPassword) a la conferéncia. Los participants deuràn fornir lo $t(lockRoomPassword) abans d’aver l’autorizacion de rejónher la conferéncia.", + "about": "Podètz ajustar un $t(lockRoomPassword) per rejónher una conferéncia. Los participants deuràn fornir lo $t(lockRoomPassword) abans d’obténer l’autorizacion de dintrar dins la conferéncia." + }, + "e2ee": { + "labelToolTip": "La comunicacion àudio e vidèo d’aquesta sonada es chifrada del cap a la fin" } } + + From a4ca24705680fa24336d6c77b438906cfef409d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Tue, 30 Jun 2020 08:15:08 -0500 Subject: [PATCH 010/167] Lobby required displayname (#7197) * ref: Rename jitsi_bosh_query_room to jitsi_web_query_room. This is no longer bosh only and is available for both bosh and websocket sessions. * feat: Adds feature to disco-info indicating that display name is required. * feat: Adds option to disable checking whether display name is required. * ref: Clears auth_token when verification fails. * squash: Fixing comments. * squash: Updates to latest lib-jitsi-meet. --- package-lock.json | 4 +- package.json | 2 +- resources/prosody-plugins/mod_auth_token.lua | 17 +++++- .../prosody-plugins/mod_muc_lobby_rooms.lua | 56 ++++++++++++++++++- .../prosody-plugins/mod_muc_poltergeist.lua | 4 +- .../mod_token_verification.lua | 3 +- 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index a43529acc..9d48e2403 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10724,8 +10724,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#4fec06db7fc59a88021ec0b409eda47f21c42902", - "from": "github:jitsi/lib-jitsi-meet#4fec06db7fc59a88021ec0b409eda47f21c42902", + "version": "github:jitsi/lib-jitsi-meet#8f9bd254bb3813808e6e1f7974aacc4d1414fcdb", + "from": "github:jitsi/lib-jitsi-meet#8f9bd254bb3813808e6e1f7974aacc4d1414fcdb", "requires": { "@jitsi/sdp-interop": "1.0.3", "@jitsi/sdp-simulcast": "0.3.0", diff --git a/package.json b/package.json index 269bdaf21..68b48b349 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "js-md5": "0.6.1", "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#4fec06db7fc59a88021ec0b409eda47f21c42902", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#8f9bd254bb3813808e6e1f7974aacc4d1414fcdb", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", diff --git a/resources/prosody-plugins/mod_auth_token.lua b/resources/prosody-plugins/mod_auth_token.lua index 9c8fdff3d..76db97789 100644 --- a/resources/prosody-plugins/mod_auth_token.lua +++ b/resources/prosody-plugins/mod_auth_token.lua @@ -25,15 +25,25 @@ function init_session(event) if query ~= nil then local params = formdecode(query); + + -- The following fields are filled in the session, by extracting them + -- from the query and no validation is beeing done. + -- After validating auth_token will be cleaned in case of error and few + -- other fields will be extracted from the token and set in the session + session.auth_token = query and params.token or nil; -- previd is used together with https://modules.prosody.im/mod_smacks.html -- the param is used to find resumed session and re-use anonymous(random) user id -- (see get_username_from_token) session.previd = query and params.previd or nil; - -- The room name and optional prefix from the bosh query - session.jitsi_bosh_query_room = params.room; - session.jitsi_bosh_query_prefix = params.prefix or ""; + -- The room name and optional prefix from the web query + session.jitsi_web_query_room = params.room; + session.jitsi_web_query_prefix = params.prefix or ""; + + -- Deprecated, you should use jitsi_web_query_room and jitsi_web_query_prefix + session.jitsi_bosh_query_room = session.jitsi_web_query_room; + session.jitsi_bosh_query_prefix = session.jitsi_web_query_prefix; end end @@ -72,6 +82,7 @@ function provider.get_sasl_handler(session) if (res == false) then log("warn", "Error verifying token err:%s, reason:%s", error, reason); + session.auth_token = nil; return res, error, reason; end diff --git a/resources/prosody-plugins/mod_muc_lobby_rooms.lua b/resources/prosody-plugins/mod_muc_lobby_rooms.lua index 95558c900..964ec9cf5 100644 --- a/resources/prosody-plugins/mod_muc_lobby_rooms.lua +++ b/resources/prosody-plugins/mod_muc_lobby_rooms.lua @@ -28,6 +28,9 @@ local jid_bare = require 'util.jid'.bare; local filters = require 'util.filters'; local st = require 'util.stanza'; local MUC_NS = 'http://jabber.org/protocol/muc'; +local DISCO_INFO_NS = 'http://jabber.org/protocol/disco#info'; +local DISPLAY_NAME_REQUIRED_FEATURE = 'http://jitsi.org/protocol/lobbyrooms#displayname_required'; +local LOBBY_IDENTITY_TYPE = 'lobbyrooms'; local is_healthcheck_room = module:require "util".is_healthcheck_room; @@ -42,7 +45,14 @@ if lobby_muc_component_config == nil then return ; end -local whitelist = module:get_option_set("muc_lobby_whitelist", {}); +local whitelist; +local check_display_name_required; +local function load_config() + whitelist = module:get_option_set("muc_lobby_whitelist", {}); + check_display_name_required + = module:get_option_boolean("muc_lobby_check_display_name_required", true); +end +load_config(); local lobby_muc_service; local main_muc_service; @@ -84,6 +94,9 @@ function filter_stanza(stanza) end return nil; + elseif stanza.name == 'iq' and stanza:get_child('query', DISCO_INFO_NS) then + -- allow disco info from the lobby component + return stanza; end return nil; @@ -125,7 +138,24 @@ function process_lobby_muc_loaded(lobby_muc, host_module) filters.add_filter_hook(filter_session); -- Advertise lobbyrooms support on main domain so client can pick up the address and use it - module:add_identity('component', 'lobbyrooms', lobby_muc_component_config); + module:add_identity('component', LOBBY_IDENTITY_TYPE, lobby_muc_component_config); + + -- Tag the disco#info response with a feature that display name is required + -- when the conference name from the web request has a lobby enabled. + host_module:hook("host-disco-info-node", function (event) + local session, reply, node = event.origin, event.reply, event.node; + if node == LOBBY_IDENTITY_TYPE + and session.jitsi_web_query_room + and main_muc_service + and check_display_name_required then + local room = main_muc_service.get_room_from_jid( + jid_bare(session.jitsi_web_query_room .. '@' .. main_muc_component_config)); + if room and room._data.lobbyroom then + reply:tag("feature", { var = DISPLAY_NAME_REQUIRED_FEATURE }):up(); + end + end + event.exists = true; + end); local room_mt = lobby_muc_service.room_mt; -- we base affiliations (roles) in lobby muc component to be based on the roles in the main muc @@ -256,3 +286,25 @@ process_host_module(main_muc_component_config, function(host_module, host) end end, -4); -- the default hook on members_only module is on -5 end); + +-- Extract 'room' param from URL when session is created +function update_session(event) + local session = event.session; + + if session.jitsi_web_query_room then + -- no need for an update + return; + end + + local query = event.request.url.query; + if query ~= nil then + local params = formdecode(query); + -- The room name and optional prefix from the web query + session.jitsi_web_query_room = params.room; + session.jitsi_web_query_prefix = params.prefix or ""; + end +end + +module:hook_global("bosh-session", update_session); +module:hook_global("websocket-session", update_session); +module:hook_global('config-reloaded', load_config); diff --git a/resources/prosody-plugins/mod_muc_poltergeist.lua b/resources/prosody-plugins/mod_muc_poltergeist.lua index 4d6538bd8..0868b7bd4 100644 --- a/resources/prosody-plugins/mod_muc_poltergeist.lua +++ b/resources/prosody-plugins/mod_muc_poltergeist.lua @@ -106,8 +106,8 @@ prosody.events.add_handler("pre-jitsi-authentication", function(session) if (session.jitsi_meet_context_user) then local room = get_room( - session.jitsi_bosh_query_room, - session.jitsi_bosh_query_prefix); + session.jitsi_web_query_room, + session.jitsi_web_query_prefix); if (not room) then return nil; diff --git a/resources/prosody-plugins/mod_token_verification.lua b/resources/prosody-plugins/mod_token_verification.lua index b9e459ba6..ad39557ac 100644 --- a/resources/prosody-plugins/mod_token_verification.lua +++ b/resources/prosody-plugins/mod_token_verification.lua @@ -93,7 +93,8 @@ for event_name, method in pairs { return; end - if not session.auth_token then + -- jitsi_meet_room is set after the token had been verified + if not session.auth_token or not session.jitsi_meet_room then session.send( st.error_reply( stanza, "cancel", "not-allowed", "Room modification disabled for guests")); From bfd5db355d7547ce0d2f952f1a16890b3b773508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imre=20Farag=C3=B3?= Date: Wed, 1 Jul 2020 14:42:44 +0200 Subject: [PATCH 011/167] prosody muc_size plugin, room get info error fix (Traceback[httpserver]: /usr/lib/prosody/util/async.lua:137: /prosody-plugins/mod_muc_size.lua:141: attempt to concatenate local 'subdomain' (a nil value) --- resources/prosody-plugins/mod_muc_size.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/prosody-plugins/mod_muc_size.lua b/resources/prosody-plugins/mod_muc_size.lua index f87f827ee..9aa98af1f 100644 --- a/resources/prosody-plugins/mod_muc_size.lua +++ b/resources/prosody-plugins/mod_muc_size.lua @@ -137,7 +137,7 @@ function handle_get_room (event) local room_address = jid.join(room_name, muc_domain_prefix.."."..domain_name); - if subdomain ~= "" then + if subdomain and subdomain ~= "" then room_address = "["..subdomain.."]"..room_address; end From bc43f00d289c3f13311e583980a7e9d03be6ad33 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Mon, 30 Mar 2020 08:50:26 -0500 Subject: [PATCH 012/167] feat: pass network info to LJM --- react/features/base/lib-jitsi-meet/actions.js | 8 +++++++- react/features/base/lib-jitsi-meet/middleware.js | 7 +++++++ react/features/base/net-info/selectors.js | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 react/features/base/net-info/selectors.js diff --git a/react/features/base/lib-jitsi-meet/actions.js b/react/features/base/lib-jitsi-meet/actions.js index 7fb49edc7..fbd1f6879 100644 --- a/react/features/base/lib-jitsi-meet/actions.js +++ b/react/features/base/lib-jitsi-meet/actions.js @@ -2,6 +2,8 @@ import type { Dispatch } from 'redux'; +import { isOnline } from '../net-info/selectors'; + import JitsiMeetJS from './_'; import { LIB_DID_DISPOSE, @@ -37,7 +39,8 @@ export function disposeLib() { */ export function initLib() { return (dispatch: Dispatch, getState: Function): void => { - const config = getState()['features/base/config']; + const state = getState(); + const config = state['features/base/config']; if (!config) { throw new Error('Cannot init lib-jitsi-meet without config'); @@ -50,6 +53,9 @@ export function initLib() { enableAnalyticsLogging: isAnalyticsEnabled(getState), ...config }); + JitsiMeetJS.setNetworkInfo({ + isOnline: isOnline(state) + }); dispatch({ type: LIB_DID_INIT }); } catch (error) { dispatch(libInitError(error)); diff --git a/react/features/base/lib-jitsi-meet/middleware.js b/react/features/base/lib-jitsi-meet/middleware.js index 9c17b94bb..5068d0b1e 100644 --- a/react/features/base/lib-jitsi-meet/middleware.js +++ b/react/features/base/lib-jitsi-meet/middleware.js @@ -2,6 +2,7 @@ import { SET_CONFIG } from '../config'; import { setLoggingConfig } from '../logging'; +import { SET_NETWORK_INFO } from '../net-info'; import { PARTICIPANT_LEFT } from '../participants'; import { MiddlewareRegistry } from '../redux'; @@ -31,6 +32,12 @@ MiddlewareRegistry.register(store => next => action => { } break; + case SET_NETWORK_INFO: + JitsiMeetJS.setNetworkInfo({ + isOnline: action.isOnline + }); + break; + case PARTICIPANT_LEFT: action.participant.local && store.dispatch(disposeLib()); break; diff --git a/react/features/base/net-info/selectors.js b/react/features/base/net-info/selectors.js new file mode 100644 index 000000000..776bcc37e --- /dev/null +++ b/react/features/base/net-info/selectors.js @@ -0,0 +1,11 @@ +import { STORE_NAME } from './constants'; + +/** + * A selector for the internet online status. + * + * @param {Object} state - The redux state. + * @returns {boolean} + */ +export function isOnline(state) { + return state[STORE_NAME].isOnline; +} From 1ff27b7298ace5baa16f3aa05998b690cb08d260 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Tue, 30 Jun 2020 12:48:06 -0500 Subject: [PATCH 013/167] fix: store.getState() called while the reducer is executing --- react/features/base/devices/middleware.js | 30 +++++++++++++++++++++-- react/features/base/devices/reducer.js | 20 --------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/react/features/base/devices/middleware.js b/react/features/base/devices/middleware.js index 5884695de..99fae99f3 100644 --- a/react/features/base/devices/middleware.js +++ b/react/features/base/devices/middleware.js @@ -15,14 +15,19 @@ import { NOTIFY_CAMERA_ERROR, NOTIFY_MIC_ERROR, SET_AUDIO_INPUT_DEVICE, - SET_VIDEO_INPUT_DEVICE + SET_VIDEO_INPUT_DEVICE, + UPDATE_DEVICE_LIST } from './actionTypes'; import { removePendingDeviceRequests, setAudioInputDevice, setVideoInputDevice } from './actions'; -import { formatDeviceLabel, setAudioOutputDeviceId } from './functions'; +import { + formatDeviceLabel, + groupDevicesByKind, + setAudioOutputDeviceId +} from './functions'; import logger from './logger'; const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = { @@ -41,6 +46,24 @@ const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = { } }; +/** + * Logs the current device list. + * + * @param {Object} deviceList - Whatever is returned by {@link groupDevicesByKind}. + * @returns {string} + */ +function logDeviceList(deviceList) { + const devicesToStr = list => list.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n'); + const audioInputs = devicesToStr(deviceList.audioInput); + const audioOutputs = devicesToStr(deviceList.audioOutput); + const videoInputs = devicesToStr(deviceList.videoInput); + + logger.debug('Device list updated:\n' + + `audioInput:\n${audioInputs}\n` + + `audioOutput:\n${audioOutputs}\n` + + `videoInput:\n${videoInputs}`); +} + /** * Implements the middleware of the feature base/devices. * @@ -123,6 +146,9 @@ MiddlewareRegistry.register(store => next => action => { APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId); } break; + case UPDATE_DEVICE_LIST: + logDeviceList(groupDevicesByKind(action.devices)); + break; case CHECK_AND_NOTIFY_FOR_NEW_DEVICE: _checkAndNotifyForNewDevice(store, action.newDevices, action.oldDevices); break; diff --git a/react/features/base/devices/reducer.js b/react/features/base/devices/reducer.js index 206b5e416..1a53c0538 100644 --- a/react/features/base/devices/reducer.js +++ b/react/features/base/devices/reducer.js @@ -19,24 +19,6 @@ const DEFAULT_STATE = { pendingRequests: [] }; -/** - * Logs the current device list. - * - * @param {Object} deviceList - Whatever is returned by {@link groupDevicesByKind}. - * @returns {string} - */ -function logDeviceList(deviceList) { - const devicesToStr = list => list.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n'); - const audioInputs = devicesToStr(deviceList.audioInput); - const audioOutputs = devicesToStr(deviceList.audioOutput); - const videoInputs = devicesToStr(deviceList.videoInput); - - logger.debug('Device list updated:\n' - + `audioInput:\n${audioInputs}\n` - + `audioOutput:\n${audioOutputs}\n` - + `videoInput:\n${videoInputs}`); -} - /** * Listen for actions which changes the state of known and used devices. * @@ -54,8 +36,6 @@ ReducerRegistry.register( case UPDATE_DEVICE_LIST: { const deviceList = groupDevicesByKind(action.devices); - logDeviceList(deviceList); - return { ...state, availableDevices: deviceList From 858a3d953c44bc4b03b84ca604fa6ca04a14a716 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Wed, 1 Jul 2020 15:22:34 -0500 Subject: [PATCH 014/167] deps: LJM e66cc365014cd429280a95a379ad62d993217f6b Update lib-jitsi-meet which adds the setNetworkInfo method. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d48e2403..585bf35d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10724,8 +10724,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#8f9bd254bb3813808e6e1f7974aacc4d1414fcdb", - "from": "github:jitsi/lib-jitsi-meet#8f9bd254bb3813808e6e1f7974aacc4d1414fcdb", + "version": "github:jitsi/lib-jitsi-meet#e66cc365014cd429280a95a379ad62d993217f6b", + "from": "github:jitsi/lib-jitsi-meet#e66cc365014cd429280a95a379ad62d993217f6b", "requires": { "@jitsi/sdp-interop": "1.0.3", "@jitsi/sdp-simulcast": "0.3.0", diff --git a/package.json b/package.json index 68b48b349..ca28af0fd 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "js-md5": "0.6.1", "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#8f9bd254bb3813808e6e1f7974aacc4d1414fcdb", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#e66cc365014cd429280a95a379ad62d993217f6b", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", From b10a45bf9899ebf14d6a08ea6ebe73209fa3fe38 Mon Sep 17 00:00:00 2001 From: damencho Date: Thu, 2 Jul 2020 10:30:36 -0500 Subject: [PATCH 015/167] fix: Fixes generating self-signed certificate. The wrong quotes error: req: Error on line 354 of config file "/dev/fd/63" Error Loading extension section SAN 140403719438784:error:0E06C069:configuration file routines:NCONF_get_section:no conf:../crypto/conf/conf_lib.c:245: Having the ip and specifying dns: Error Loading extension section SAN 140127168778688:error:220A4076:X509 V3 routines:a2i_GENERAL_NAME:bad ip address:../crypto/x509v3/v3_alt.c:457:value=jitsi.example.com 140127168778688:error:22098080:X509 V3 routines:X509V3_EXT_nconf:error in extension:../crypto/x509v3/v3_conf.c:47:name=subjectAltName, value=DNS:localhost,DNS:jitsi.example.com,IP:jitsi.example.com --- debian/jitsi-meet-web-config.postinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/jitsi-meet-web-config.postinst b/debian/jitsi-meet-web-config.postinst index 8d54c9145..c5199ae59 100644 --- a/debian/jitsi-meet-web-config.postinst +++ b/debian/jitsi-meet-web-config.postinst @@ -98,7 +98,7 @@ case "$1" in -reqexts SAN \ -extensions SAN \ -config <(cat /etc/ssl/openssl.cnf \ - <(printf '[SAN]\nsubjectAltName=DNS:localhost,DNS:$JVB_HOSTNAME,IP:$JVB_HOSTNAME')) + <(printf "[SAN]\nsubjectAltName=DNS:localhost,DNS:$JVB_HOSTNAME")) fi fi From 6d3d15a64b7d82cb7abf04dc0d567dbea94b21aa Mon Sep 17 00:00:00 2001 From: damencho Date: Mon, 15 Jun 2020 11:55:21 -0500 Subject: [PATCH 016/167] feat: Adds an option to validate a recording token. --- .../prosody-plugins/mod_filter_iq_jibri.lua | 39 +++++++++++++++---- resources/prosody-plugins/token/util.lib.lua | 23 +++++++---- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/resources/prosody-plugins/mod_filter_iq_jibri.lua b/resources/prosody-plugins/mod_filter_iq_jibri.lua index c211de6d3..f61b539ad 100644 --- a/resources/prosody-plugins/mod_filter_iq_jibri.lua +++ b/resources/prosody-plugins/mod_filter_iq_jibri.lua @@ -1,5 +1,8 @@ local st = require "util.stanza"; local is_feature_allowed = module:require "util".is_feature_allowed; +local token_util = module:require "token/util".new(module); + +local accepted_rayo_iq_token_issuers = module:get_option_array("accepted_rayo_iq_token_issuers"); -- filters jibri iq in case of requested from jwt authenticated session that -- has features in the user context, but without feature for recording @@ -11,15 +14,37 @@ module:hook("pre-iq/full", function(event) local session = event.origin; local token = session.auth_token; - if jibri.attr.action == 'start' - and (token == nil + if jibri.attr.action == 'start' then + local errorReason; + if accepted_rayo_iq_token_issuers then + local iq_token = jibri.attr.token; + if iq_token then + local session = {}; + session.auth_token = iq_token; + local verified, reason = token_util:process_and_verify_token( + session, accepted_rayo_iq_token_issuers); + if verified then + return nil; -- this will proceed with dispatching the stanza + end + errorReason = reason; + else + errorReason = 'No recording token provided'; + end + + module:log("warn", "not a valid token %s", tostring(errorReason)); + session.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end + + if token == nil or not is_feature_allowed(session, - (jibri.attr.recording_mode == 'file' and 'recording' or 'livestreaming')) + (jibri.attr.recording_mode == 'file' and 'recording' or 'livestreaming') ) then - module:log("info", - "Filtering jibri start recording, stanza:%s", tostring(stanza)); - session.send(st.error_reply(stanza, "auth", "forbidden")); - return true; + module:log("info", + "Filtering jibri start recording, stanza:%s", tostring(stanza)); + session.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end end end end diff --git a/resources/prosody-plugins/token/util.lib.lua b/resources/prosody-plugins/token/util.lib.lua index 72af078db..4c53df85b 100644 --- a/resources/prosody-plugins/token/util.lib.lua +++ b/resources/prosody-plugins/token/util.lib.lua @@ -159,9 +159,10 @@ end --- Verifies issuer part of token -- @param 'iss' claim from the token to verify +-- @param 'acceptedIssuers' list of issuers to check -- @return nil and error string or true for accepted claim -function Util:verify_issuer(issClaim) - for i, iss in ipairs(self.acceptedIssuers) do +function Util:verify_issuer(issClaim, acceptedIssuers) + for i, iss in ipairs(acceptedIssuers) do if issClaim == iss then --claim matches an accepted issuer so return success return true; @@ -192,8 +193,9 @@ end --- Verifies token -- @param token the token to verify -- @param secret the secret to use to verify token +-- @param acceptedIssuers the list of accepted issuers to check -- @return nil and error or the extracted claims from the token -function Util:verify_token(token, secret) +function Util:verify_token(token, secret, acceptedIssuers) local claims, err = jwt.decode(token, secret, true); if claims == nil then return nil, err; @@ -209,7 +211,7 @@ function Util:verify_token(token, secret) return nil, "'iss' claim is missing"; end --check the issuer against the accepted list - local issCheck, issCheckErr = self:verify_issuer(issClaim); + local issCheck, issCheckErr = self:verify_issuer(issClaim, acceptedIssuers); if issCheck == nil then return nil, issCheckErr; end @@ -241,8 +243,13 @@ end -- session.jitsi_meet_context_group - the group value from the token -- session.jitsi_meet_context_features - the features value from the token -- @param session the current session +-- @param acceptedIssuers optional list of accepted issuers to check -- @return false and error -function Util:process_and_verify_token(session) +function Util:process_and_verify_token(session, acceptedIssuers) + if not acceptedIssuers then + acceptedIssuers = self.acceptedIssuers; + end + if session.auth_token == nil then if self.allowEmptyToken then return true; @@ -272,9 +279,9 @@ function Util:process_and_verify_token(session) -- now verify the whole token local claims, msg; if self.asapKeyServer then - claims, msg = self:verify_token(session.auth_token, pubKey); + claims, msg = self:verify_token(session.auth_token, pubKey, acceptedIssuers); else - claims, msg = self:verify_token(session.auth_token, self.appSecret); + claims, msg = self:verify_token(session.auth_token, self.appSecret, acceptedIssuers); end if claims ~= nil then -- Binds room name to the session which is later checked on MUC join @@ -401,4 +408,4 @@ function Util:verify_room(session, room_address) end end -return Util; \ No newline at end of file +return Util; From da9a70129e443dff676fafa61805b6cf62d516cc Mon Sep 17 00:00:00 2001 From: Hristo Terezov Date: Thu, 2 Jul 2020 13:14:10 -0500 Subject: [PATCH 017/167] Revert: clean-css update due to broken paths. --- package-lock.json | 40 ++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 585bf35d9..e4464aef8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6145,31 +6145,33 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" }, - "clean-css-cli": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.3.0.tgz", - "integrity": "sha512-8GHZfr+mG3zB/Lgqrr27qHBFsPSn0fyEI3f2rIZpxPxUbn2J6A8xyyeBRVTW8duDuXigN0s80vsXiXJOEFIO5Q==", + "clean-css": { + "version": "3.4.25", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.25.tgz", + "integrity": "sha1-nppS1cHmvFEj4bJ4P6Zf6ViUbt4=", "dev": true, "requires": { - "clean-css": "^4.2.1", - "commander": "2.x", - "glob": "7.x" + "commander": "2.8.x", + "source-map": "0.4.x" }, "dependencies": { - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { - "source-map": "~0.6.0" + "graceful-readlink": ">= 1.0.0" } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } } } }, @@ -9291,6 +9293,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", diff --git a/package.json b/package.json index ca28af0fd..2965ec3ea 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "babel-eslint": "10.0.1", "babel-loader": "8.0.4", "circular-dependency-plugin": "5.2.0", - "clean-css-cli": "4.3.0", + "clean-css": "3.4.25", "css-loader": "3.6.0", "eslint": "5.6.1", "eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#1.0.3", From a7e0df262320cd8370c06d170bdf37aea7096a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 3 Jul 2020 10:29:00 +0200 Subject: [PATCH 018/167] toolbox: fix missing key prop Fixes a React warning. --- react/features/toolbox/components/web/Toolbox.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js index 2c4dfc336..08c3f1f5c 100644 --- a/react/features/toolbox/components/web/Toolbox.js +++ b/react/features/toolbox/components/web/Toolbox.js @@ -1083,7 +1083,11 @@ class Toolbox extends Component { } /> ); case 'closedcaptions': - return ; + return ( + + ); case 'security': return ( Date: Fri, 3 Jul 2020 10:36:58 +0200 Subject: [PATCH 019/167] lang: update French translation --- lang/main-fr.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lang/main-fr.json b/lang/main-fr.json index ce94d2d44..706ab6314 100644 --- a/lang/main-fr.json +++ b/lang/main-fr.json @@ -783,5 +783,32 @@ }, "helpView": { "header": "Centre d'aide" + }, + "lobby": { + "allow": "Autoriser", + "backToKnockModeButton": "Aucun mot de passe, demander à rejoindre plutôt", + "dialogTitle": "Mode lobby", + "disableDialogContent": "Le mode lobby est actuellement activé. Cette fonctionnalité garantit que les participants indésirables ne peuvent pas rejoindre votre réunion. Souhaitez-vous la désactiver?", + "disableDialogSubmit": "Désactiver", + "emailField": "Saisissez votre adresse email", + "enableDialogPasswordField": "Définir le mot de passe (optionel)", + "enableDialogSubmit": "Activer", + "enableDialogText": "Le mode lobby vous permet de protéger votre réunion en n'autorisant les personnes à entrer qu'après l'approbation formelle d'un modérateur.", + "enterPasswordButton": "Saisissez un mot de passe de réunion", + "enterPasswordTitle": "Saisissez le mot de passe pour rejoindre la réunion", + "invalidPassword": "Mot de passe invalide", + "joiningMessage": "Vous allez rejoindre une réunion dès que quelqu'un aura accepté votre demande", + "joinWithPasswordMessage": "Tentative de rejoindre avec mot de passe, patientez s'il vous plait...", + "joinRejectedMessage": "Votre requête pour rejoindre une réunion a été refusée par un modérateur.", + "joinTitle": "Rejoindre une réunion", + "joiningTitle": "Demander à rejoindre une réunion...", + "joiningWithPasswordTitle": "Rejoindre avec mot de passe...", + "knockButton": "Demander à rejoindre", + "knockTitle": "Quelqu'un souhaite rejoindre la réunion", + "nameField": "Saisissez votre nom", + "passwordField": "Saisissez le mot de passe de la réunion", + "passwordJoinButton": "Rejoindre", + "reject": "Refuser", + "toggleLabel": "Activer le lobby" } } From a5f17a80330e96211689fb56886f46ab6ad246b7 Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Fri, 3 Jul 2020 10:08:21 +0300 Subject: [PATCH 020/167] feat(prejoin): Show avatar image on prejoin screen --- .../base/premeeting/components/web/PreMeetingScreen.js | 8 +++++++- react/features/base/premeeting/components/web/Preview.js | 1 + react/features/prejoin/components/Prejoin.js | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/react/features/base/premeeting/components/web/PreMeetingScreen.js b/react/features/base/premeeting/components/web/PreMeetingScreen.js index 7f21ab104..eeed03bf9 100644 --- a/react/features/base/premeeting/components/web/PreMeetingScreen.js +++ b/react/features/base/premeeting/components/web/PreMeetingScreen.js @@ -19,6 +19,11 @@ type Props = { */ footer?: React$Node, + /** + * The name of the participant. + */ + name?: string, + /** * Title of the screen. */ @@ -46,13 +51,14 @@ export default class PreMeetingScreen extends PureComponent { * @inheritdoc */ render() { - const { title, videoMuted, videoTrack } = this.props; + const { name, title, videoMuted, videoTrack } = this.props; return (
diff --git a/react/features/base/premeeting/components/web/Preview.js b/react/features/base/premeeting/components/web/Preview.js index 830de6863..7bd8fd1cd 100644 --- a/react/features/base/premeeting/components/web/Preview.js +++ b/react/features/base/premeeting/components/web/Preview.js @@ -51,6 +51,7 @@ function Preview(props: Props) {
); diff --git a/react/features/prejoin/components/Prejoin.js b/react/features/prejoin/components/Prejoin.js index b0fefe6fa..3bdb59087 100644 --- a/react/features/prejoin/components/Prejoin.js +++ b/react/features/prejoin/components/Prejoin.js @@ -228,6 +228,7 @@ class Prejoin extends Component { return ( From ea2ea89ef76e36505f2f1114f12c1917a91f6803 Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Mon, 29 Jun 2020 15:45:47 +0300 Subject: [PATCH 021/167] fix(prejoin): dialout popup buttons --- css/_prejoin-dialog.scss | 5 ++ css/_premeeting-screens.scss | 122 +++++++++++++++++------------------ 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/css/_prejoin-dialog.scss b/css/_prejoin-dialog.scss index 5c084bcfc..3e77ca271 100644 --- a/css/_prejoin-dialog.scss +++ b/css/_prejoin-dialog.scss @@ -96,6 +96,11 @@ padding: 0 8px; } } + + .prejoin-dialog-btn.primary, + .action-btn.prejoin-dialog-btn.text { + width: 310px; + } } .prejoin-dialog-callout { diff --git a/css/_premeeting-screens.scss b/css/_premeeting-screens.scss index a93691854..de1f76afa 100644 --- a/css/_premeeting-screens.scss +++ b/css/_premeeting-screens.scss @@ -1,7 +1,7 @@ /** * Shared style for full screen local track based dialogs/modals. */ - .premeeting-screen { +.premeeting-screen { align-items: stretch; background: #1C2025; bottom: 0; @@ -14,6 +14,66 @@ top: 0; z-index: $toolbarZ + 1; + .action-btn { + border-radius: 3px; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 15px; + line-height: 24px; + margin-top: 16px; + padding: 7px 16px; + position: relative; + text-align: center; + width: 286px; + + &.primary { + background: #0376DA; + border: 1px solid #0376DA; + } + + &.secondary { + background: transparent; + border: 1px solid #5E6D7A; + } + + &.text { + width: auto; + font-size: 13px; + margin: 0; + padding: 0; + } + + &.disabled { + background: #5E6D7A; + border: 1px solid #5E6D7A; + color: #AFB6BC; + cursor: initial; + + .icon { + & > svg { + fill: #AFB6BC; + } + } + + .options { + border-left: 1px solid #AFB6BC; + } + } + + .options { + align-items: center; + border-left: 1px solid #fff; + display: flex; + height: 100%; + justify-content: center; + position: absolute; + right: 0; + top: 0; + width: 40px; + } + } + .content { align-items: center; background-image: linear-gradient(transparent, black); @@ -97,66 +157,6 @@ color: $defaultWarningColor; } } - - .action-btn { - border-radius: 3px; - color: #fff; - cursor: pointer; - display: inline-block; - font-size: 15px; - line-height: 24px; - margin-top: 16px; - padding: 7px 16px; - position: relative; - text-align: center; - width: 286px; - - &.primary { - background: #0376DA; - border: 1px solid #0376DA; - } - - &.secondary { - background: transparent; - border: 1px solid #5E6D7A; - } - - &.text { - width: auto; - font-size: 13px; - margin: 0; - padding: 0; - } - - &.disabled { - background: #5E6D7A; - border: 1px solid #5E6D7A; - color: #AFB6BC; - cursor: initial; - - .icon { - & > svg { - fill: #AFB6BC; - } - } - - .options { - border-left: 1px solid #AFB6BC; - } - } - - .options { - align-items: center; - border-left: 1px solid #fff; - display: flex; - height: 100%; - justify-content: center; - position: absolute; - right: 0; - top: 0; - width: 40px; - } - } } .media-btn-container { From 5f579e9a154b9a75427404c3d11ab2ae35ba7d82 Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Thu, 2 Jul 2020 12:18:38 +0300 Subject: [PATCH 022/167] fix(prejoin): Make display name mandatory only for lobby A user should not be forced to enter a display name if the lobby is not enabled for the room. --- connection.js | 13 +++++++++++++ react/features/prejoin/actionTypes.js | 5 +++++ react/features/prejoin/actions.js | 12 ++++++++++++ react/features/prejoin/components/Prejoin.js | 17 ++++++++++++++--- react/features/prejoin/functions.js | 11 +++++++++++ react/features/prejoin/reducer.js | 15 ++++++++++++--- 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/connection.js b/connection.js index 8c92bfb97..1f8b43117 100644 --- a/connection.js +++ b/connection.js @@ -13,6 +13,7 @@ import { JitsiConnectionErrors, JitsiConnectionEvents } from './react/features/base/lib-jitsi-meet'; +import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions'; const logger = Logger.getLogger(__filename); @@ -113,6 +114,10 @@ function connect(id, password, roomName) { connection.addEventListener( JitsiConnectionEvents.CONNECTION_FAILED, connectionFailedHandler); + connection.addEventListener( + JitsiConnectionEvents.DISPLAY_NAME_REQUIRED, + displayNameRequiredHandler + ); /* eslint-disable max-params */ /** @@ -166,6 +171,14 @@ function connect(id, password, roomName) { reject(err); } + /** + * Marks the display name for the prejoin screen as required. + * This can happen if a user tries to join a room with lobby enabled. + */ + function displayNameRequiredHandler() { + APP.store.dispatch(setPrejoinDisplayNameRequired()); + } + checkForAttachParametersAndConnect(id, password, connection); }); } diff --git a/react/features/prejoin/actionTypes.js b/react/features/prejoin/actionTypes.js index 8ab5f4632..6f9667170 100644 --- a/react/features/prejoin/actionTypes.js +++ b/react/features/prejoin/actionTypes.js @@ -14,6 +14,11 @@ export const SET_DEVICE_STATUS = 'SET_DEVICE_STATUS'; */ export const SET_SKIP_PREJOIN = 'SET_SKIP_PREJOIN'; +/** + * Action type used to set the mandatory stance of the prejoin display name. + */ +export const SET_PREJOIN_DISPLAY_NAME_REQUIRED = 'SET_PREJOIN_DISPLAY_NAME_REQUIRED'; + /** * Action type to set the country to dial out to. */ diff --git a/react/features/prejoin/actions.js b/react/features/prejoin/actions.js index 2c0129f84..34a9d0ef1 100644 --- a/react/features/prejoin/actions.js +++ b/react/features/prejoin/actions.js @@ -21,6 +21,7 @@ import { SET_DIALOUT_COUNTRY, SET_DIALOUT_NUMBER, SET_DIALOUT_STATUS, + SET_PREJOIN_DISPLAY_NAME_REQUIRED, SET_SKIP_PREJOIN, SET_JOIN_BY_PHONE_DIALOG_VISIBLITY, SET_PREJOIN_DEVICE_ERRORS, @@ -342,6 +343,17 @@ export function setDialOutCountry(value: Object) { }; } +/** + * Action used to set the stance of the display name. + * + * @returns {Object} + */ +export function setPrejoinDisplayNameRequired() { + return { + type: SET_PREJOIN_DISPLAY_NAME_REQUIRED + }; +} + /** * Action used to set the dial out number. * diff --git a/react/features/prejoin/components/Prejoin.js b/react/features/prejoin/components/Prejoin.js index 3bdb59087..830f1b586 100644 --- a/react/features/prejoin/components/Prejoin.js +++ b/react/features/prejoin/components/Prejoin.js @@ -18,8 +18,9 @@ import { setJoinByPhoneDialogVisiblity as setJoinByPhoneDialogVisiblityAction } from '../actions'; import { - isJoinByPhoneButtonVisible, isDeviceStatusVisible, + isDisplayNameRequired, + isJoinByPhoneButtonVisible, isJoinByPhoneDialogVisible } from '../functions'; @@ -38,6 +39,11 @@ type Props = { */ hasJoinByPhoneButton: boolean, + /** + * If join button is disabled or not. + */ + joinButtonDisabled: boolean, + /** * Joins the current meeting. */ @@ -212,6 +218,7 @@ class Prejoin extends Component { */ render() { const { + joinButtonDisabled, hasJoinByPhoneButton, joinConference, joinConferenceWithoutAudio, @@ -265,7 +272,7 @@ class Prejoin extends Component { isOpen = { showJoinByPhoneButtons } onClose = { _onDropdownClose }> { * @returns {Object} */ function mapStateToProps(state): Object { + const name = getDisplayName(state); + const joinButtonDisabled = isDisplayNameRequired(state) && !name; + return { + joinButtonDisabled, + name, deviceStatusVisible: isDeviceStatusVisible(state), - name: getDisplayName(state), roomName: getRoomName(state), showDialog: isJoinByPhoneDialogVisible(state), hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state), diff --git a/react/features/prejoin/functions.js b/react/features/prejoin/functions.js index 75863939c..708b1e1a4 100644 --- a/react/features/prejoin/functions.js +++ b/react/features/prejoin/functions.js @@ -25,6 +25,17 @@ export function isDeviceStatusVisible(state: Object): boolean { && !state['features/base/config'].startSilent; } +/** + * Selector for determining if the display name is mandatory. + * + * @param {Object} state - The state of the app. + * @returns {boolean} + */ +export function isDisplayNameRequired(state: Object): boolean { + return state['features/prejoin'].isDisplayNameRequired + || state['features/base/config'].requireDisplayName; +} + /** * Returns the text for the prejoin status bar. * diff --git a/react/features/prejoin/reducer.js b/react/features/prejoin/reducer.js index f00860657..599560807 100644 --- a/react/features/prejoin/reducer.js +++ b/react/features/prejoin/reducer.js @@ -2,13 +2,14 @@ import { ReducerRegistry } from '../base/redux'; import { SET_DEVICE_STATUS, - SET_DIALOUT_NUMBER, SET_DIALOUT_COUNTRY, + SET_DIALOUT_NUMBER, SET_DIALOUT_STATUS, SET_JOIN_BY_PHONE_DIALOG_VISIBLITY, - SET_SKIP_PREJOIN, SET_PREJOIN_DEVICE_ERRORS, - SET_PREJOIN_PAGE_VISIBILITY + SET_PREJOIN_DISPLAY_NAME_REQUIRED, + SET_PREJOIN_PAGE_VISIBILITY, + SET_SKIP_PREJOIN } from './actionTypes'; const DEFAULT_STATE = { @@ -22,6 +23,7 @@ const DEFAULT_STATE = { }, dialOutNumber: '', dialOutStatus: 'prejoin.dialing', + isDisplayNameRequired: false, name: '', rawError: '', showPrejoin: true, @@ -94,6 +96,13 @@ ReducerRegistry.register( }; } + case SET_PREJOIN_DISPLAY_NAME_REQUIRED: { + return { + ...state, + isDisplayNameRequired: true + }; + } + default: return state; } From b3a29058496397a411eaff462644f1a83922e5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Fri, 3 Jul 2020 08:26:44 -0500 Subject: [PATCH 023/167] feat: Sends json messages notifying for lobby actions. (#7209) * feat: Sends json messages notifying for lobby actions. * squash: Fixes quotes to be consistent. * fix: Fixes attempt to call global 'formdecode' (a nil value). --- .../prosody-plugins/mod_muc_lobby_rooms.lua | 113 ++++++++++++++---- 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/resources/prosody-plugins/mod_muc_lobby_rooms.lua b/resources/prosody-plugins/mod_muc_lobby_rooms.lua index 964ec9cf5..024bf7c6b 100644 --- a/resources/prosody-plugins/mod_muc_lobby_rooms.lua +++ b/resources/prosody-plugins/mod_muc_lobby_rooms.lua @@ -16,23 +16,29 @@ -- muc_room_default_public_jids = true -- -- we use async to detect Prosody 0.10 and earlier -local have_async = pcall(require, "util.async"); +local have_async = pcall(require, 'util.async'); if not have_async then - module:log("warn", "Lobby rooms will not work with Prosody version 0.10 or less."); + module:log('warn', 'Lobby rooms will not work with Prosody version 0.10 or less.'); return; end +local formdecode = require "util.http".formdecode; local jid_split = require 'util.jid'.split; local jid_bare = require 'util.jid'.bare; +local json = require 'util.json'; local filters = require 'util.filters'; local st = require 'util.stanza'; local MUC_NS = 'http://jabber.org/protocol/muc'; local DISCO_INFO_NS = 'http://jabber.org/protocol/disco#info'; local DISPLAY_NAME_REQUIRED_FEATURE = 'http://jitsi.org/protocol/lobbyrooms#displayname_required'; local LOBBY_IDENTITY_TYPE = 'lobbyrooms'; +local NOTIFY_JSON_MESSAGE_TYPE = 'lobby-notify'; +local NOTIFY_LOBBY_ENABLED = 'LOBBY-ENABLED'; +local NOTIFY_LOBBY_ACCESS_GRANTED = 'LOBBY-ACCESS-GRANTED'; +local NOTIFY_LOBBY_ACCESS_DENIED = 'LOBBY-ACCESS-DENIED'; -local is_healthcheck_room = module:require "util".is_healthcheck_room; +local is_healthcheck_room = module:require 'util'.is_healthcheck_room; local main_muc_component_config = module:get_option_string('main_muc'); if main_muc_component_config == nil then @@ -48,23 +54,23 @@ end local whitelist; local check_display_name_required; local function load_config() - whitelist = module:get_option_set("muc_lobby_whitelist", {}); + whitelist = module:get_option_set('muc_lobby_whitelist', {}); check_display_name_required - = module:get_option_boolean("muc_lobby_check_display_name_required", true); + = module:get_option_boolean('muc_lobby_check_display_name_required', true); end load_config(); local lobby_muc_service; local main_muc_service; --- Checks whether there is self-status 110 of the Date: Fri, 3 Jul 2020 17:36:04 +0200 Subject: [PATCH 024/167] turn: update default coturn configuration --- doc/debian/jitsi-meet-turn/turnserver.conf | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/debian/jitsi-meet-turn/turnserver.conf b/doc/debian/jitsi-meet-turn/turnserver.conf index 7b6590079..f37c6db50 100644 --- a/doc/debian/jitsi-meet-turn/turnserver.conf +++ b/doc/debian/jitsi-meet-turn/turnserver.conf @@ -5,7 +5,10 @@ static-auth-secret=__turnSecret__ realm=jitsi-meet.example.com cert=/etc/jitsi/meet/jitsi-meet.example.com.crt pkey=/etc/jitsi/meet/jitsi-meet.example.com.key - +no-multicast-peers +no-cli +no-loopback-peers +no-tcp-relay no-tcp listening-port=4446 tls-listening-port=4445 @@ -14,5 +17,19 @@ no-tlsv1 no-tlsv1_1 # https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4 cipher-list=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 - +denied-peer-ip=0.0.0.0-0.255.255.255 +denied-peer-ip=10.0.0.0-10.255.255.255 +denied-peer-ip=100.64.0.0-100.127.255.255 +denied-peer-ip=127.0.0.0-127.255.255.255 +denied-peer-ip=169.254.0.0-169.254.255.255 +denied-peer-ip=127.0.0.0-127.255.255.255 +denied-peer-ip=172.16.0.0-172.31.255.255 +denied-peer-ip=192.0.0.0-192.0.0.255 +denied-peer-ip=192.0.2.0-192.0.2.255 +denied-peer-ip=192.88.99.0-192.88.99.255 +denied-peer-ip=192.168.0.0-192.168.255.255 +denied-peer-ip=198.18.0.0-198.19.255.255 +denied-peer-ip=198.51.100.0-198.51.100.255 +denied-peer-ip=203.0.113.0-203.0.113.255 +denied-peer-ip=240.0.0.0-255.255.255.255 syslog From f73e9947c0ee6863ad053c3b43a17b017f8ecd6f Mon Sep 17 00:00:00 2001 From: damencho Date: Mon, 6 Jul 2020 08:32:11 -0500 Subject: [PATCH 025/167] fix: Uses room jids for the lobby notifications. --- resources/prosody-plugins/mod_muc_lobby_rooms.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/resources/prosody-plugins/mod_muc_lobby_rooms.lua b/resources/prosody-plugins/mod_muc_lobby_rooms.lua index 024bf7c6b..f3a912fbe 100644 --- a/resources/prosody-plugins/mod_muc_lobby_rooms.lua +++ b/resources/prosody-plugins/mod_muc_lobby_rooms.lua @@ -221,7 +221,7 @@ function process_lobby_muc_loaded(lobby_muc, host_module) local actor, occupant, room, x = event.actor, event.occupant, event.room, event.x; if check_status(x, '307') then -- we need to notify in the main room - notify_lobby_access(room.main_room, actor, occupant.jid, false); + notify_lobby_access(room.main_room, actor, occupant.nick, false); end end); end @@ -346,7 +346,12 @@ process_host_module(main_muc_component_config, function(host_module, host) local from = stanza:get_child('x', 'http://jabber.org/protocol/muc#user') :get_child('invite').attr.from; - notify_lobby_access(room, from, invitee, true); + if room._data.lobbyroom then + local occupant = room._data.lobbyroom:get_occupant_by_real_jid(invitee); + if occupant then + notify_lobby_access(room, from, occupant.nick, true); + end + end end); end); From 873ede0e06d5fe04a90fcbb390b203afd24d510b Mon Sep 17 00:00:00 2001 From: Bettenbuk Zoltan Date: Mon, 6 Jul 2020 16:54:33 +0200 Subject: [PATCH 026/167] feat: lobby related notifications --- lang/main.json | 5 +++ react/features/lobby/functions.js | 29 +++++++++++++++++ react/features/lobby/middleware.js | 52 ++++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/lang/main.json b/lang/main.json index a6183abf3..a552db864 100644 --- a/lang/main.json +++ b/lang/main.json @@ -885,6 +885,11 @@ "knockButton": "Ask to Join", "knockTitle": "Someone wants to join the meeting", "nameField": "Enter your name", + "notificationLobbyAccessDenied": "{{targetParticipantName}} has been rejected to join by {{originParticipantName}}", + "notificationLobbyAccessGranted": "{{targetParticipantName}} has been allowed to join by {{originParticipantName}}", + "notificationLobbyDisabled": "Lobby has been disabled by {{originParticipantName}}", + "notificationLobbyEnabled": "Lobby has been enabled by {{originParticipantName}}", + "notificationTitle": "Lobby", "passwordField": "Enter meeting password", "passwordJoinButton": "Join", "reject": "Reject", diff --git a/react/features/lobby/functions.js b/react/features/lobby/functions.js index 151c93340..3db19489e 100644 --- a/react/features/lobby/functions.js +++ b/react/features/lobby/functions.js @@ -1,6 +1,23 @@ // @flow import { getCurrentConference } from '../base/conference'; +import { toState } from '../base/redux'; + +const JID_PATTERN = '[^@]+@[^/]+/(.+)'; + +/** + * Returns a knocking participant by ID or JID. + * + * @param {Function | Object} stateful - The Redux state or a function that resolves to the Redux state. + * @param {string} id - The ID or JID of the participant. + * @returns {Object} + */ +export function getKnockingParticipantById(stateful: Function | Object, id: string): Object { + const { knockingParticipants } = toState(stateful)['features/lobby']; + const idToFind = getIdFromJid(id) || id; + + return knockingParticipants.find(p => p.id === idToFind); +} /** * Approves (lets in) or rejects a knocking participant. @@ -21,3 +38,15 @@ export function setKnockingParticipantApproval(getState: Function, id: string, a } } } + +/** + * Parses an ID from a JID, if a JID is provided, undefined otherwise. + * + * @param {string} jid - The JID to get the ID from. + * @returns {?string} + */ +function getIdFromJid(jid: string): ?string { + const match = new RegExp(JID_PATTERN, 'g').exec(jid) || []; + + return match[1]; +} diff --git a/react/features/lobby/middleware.js b/react/features/lobby/middleware.js index 0542285ba..da974bcec 100644 --- a/react/features/lobby/middleware.js +++ b/react/features/lobby/middleware.js @@ -2,7 +2,7 @@ import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference'; import { JitsiConferenceErrors, JitsiConferenceEvents } from '../base/lib-jitsi-meet'; -import { getFirstLoadableAvatarUrl } from '../base/participants'; +import { getFirstLoadableAvatarUrl, getParticipantDisplayName } from '../base/participants'; import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; import { NOTIFICATION_TYPE, showNotification } from '../notifications'; import { isPrejoinPageEnabled } from '../prejoin/functions'; @@ -17,6 +17,7 @@ import { startKnocking, setPasswordJoinFailed } from './actions'; +import { getKnockingParticipantById } from './functions'; MiddlewareRegistry.register(store => next => action => { switch (action.type) { @@ -43,7 +44,7 @@ MiddlewareRegistry.register(store => next => action => { */ StateListenerRegistry.register( state => state['features/base/conference'].conference, - (conference, { dispatch }, previousConference) => { + (conference, { dispatch, getState }, previousConference) => { if (conference && !previousConference) { conference.on(JitsiConferenceEvents.MEMBERS_ONLY_CHANGED, enabled => { dispatch(setLobbyModeEnabled(enabled)); @@ -66,6 +67,13 @@ StateListenerRegistry.register( conference.on(JitsiConferenceEvents.LOBBY_USER_LEFT, id => { dispatch(knockingParticipantLeft(id)); }); + + conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, (origin, sender) => + _maybeSendLobbyNotification(origin, sender, { + dispatch, + getState + }) + ); } }); @@ -151,3 +159,43 @@ function _findLoadableAvatarForKnockingParticipant({ dispatch, getState }, { id }); } } + +/** + * Check the endpoint message that arrived through the conference and + * sends a lobby notification, if the message belongs to the feature. + * + * @param {Object} origin - The origin (initiator) of the message. + * @param {Object} message - The actual message. + * @param {Object} store - The Redux store. + * @returns {void} + */ +function _maybeSendLobbyNotification(origin, message, { dispatch, getState }) { + if (!origin?._id || message?.type !== 'lobby-notify') { + return; + } + + const notificationProps: any = { + descriptionArguments: { + originParticipantName: getParticipantDisplayName(getState, origin._id) + }, + titleKey: 'lobby.notificationTitle' + }; + + switch (message.event) { + case 'LOBBY-ENABLED': + notificationProps.descriptionKey = `lobby.notificationLobby${message.value ? 'En' : 'Dis'}abled`; + break; + case 'LOBBY-ACCESS-GRANTED': + notificationProps.descriptionKey = 'lobby.notificationLobbyAccessGranted'; + notificationProps.descriptionArguments.targetParticipantName + = getKnockingParticipantById(getState, message.value)?.name; + break; + case 'LOBBY-ACCESS-DENIED': + notificationProps.descriptionKey = 'lobby.notificationLobbyAccessDenied'; + notificationProps.descriptionArguments.targetParticipantName + = getKnockingParticipantById(getState, message.value)?.name; + break; + } + + dispatch(showNotification(notificationProps, 5000)); +} From 4cfc8cd7a28f1df8d55aa3ce8838a94b5880b36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 3 Jul 2020 10:26:43 +0200 Subject: [PATCH 027/167] deps: update clean-css (reland) Fix the incorrect paths (was a breaking change in version 4) by using the `--skip-rebase` cli option. --- Makefile | 2 +- package-lock.json | 40 ++++++++++++++++------------------------ package.json | 2 +- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 4cbbdfea7..998b2fc19 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ deploy-rnnoise-binary: deploy-css: $(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \ - $(CLEANCSS) $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \ + $(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \ rm $(STYLES_BUNDLE) deploy-local: diff --git a/package-lock.json b/package-lock.json index e4464aef8..585bf35d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6145,33 +6145,31 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" }, - "clean-css": { - "version": "3.4.25", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.25.tgz", - "integrity": "sha1-nppS1cHmvFEj4bJ4P6Zf6ViUbt4=", + "clean-css-cli": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.3.0.tgz", + "integrity": "sha512-8GHZfr+mG3zB/Lgqrr27qHBFsPSn0fyEI3f2rIZpxPxUbn2J6A8xyyeBRVTW8duDuXigN0s80vsXiXJOEFIO5Q==", "dev": true, "requires": { - "commander": "2.8.x", - "source-map": "0.4.x" + "clean-css": "^4.2.1", + "commander": "2.x", + "glob": "7.x" }, "dependencies": { - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", "dev": true, "requires": { - "graceful-readlink": ">= 1.0.0" + "source-map": "~0.6.0" } }, "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -9293,12 +9291,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", diff --git a/package.json b/package.json index 2965ec3ea..ca28af0fd 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "babel-eslint": "10.0.1", "babel-loader": "8.0.4", "circular-dependency-plugin": "5.2.0", - "clean-css": "3.4.25", + "clean-css-cli": "4.3.0", "css-loader": "3.6.0", "eslint": "5.6.1", "eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#1.0.3", From d5832f226d9bfc1d0fc17626dd6f5bc0424275a4 Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Tue, 7 Jul 2020 11:52:17 +0300 Subject: [PATCH 028/167] fix(dialout) whitelist dialout flag --- react/features/base/config/interfaceConfigWhitelist.js | 1 + 1 file changed, 1 insertion(+) diff --git a/react/features/base/config/interfaceConfigWhitelist.js b/react/features/base/config/interfaceConfigWhitelist.js index d417100f4..6e74d90ad 100644 --- a/react/features/base/config/interfaceConfigWhitelist.js +++ b/react/features/base/config/interfaceConfigWhitelist.js @@ -24,6 +24,7 @@ export default [ 'DISABLE_TRANSCRIPTION_SUBTITLES', 'DISABLE_VIDEO_BACKGROUND', 'DISPLAY_WELCOME_PAGE_CONTENT', + 'ENABLE_DIAL_OUT', 'ENABLE_FEEDBACK_ANIMATION', 'ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT', 'FILM_STRIP_MAX_HEIGHT', From 8a19a34d190ebb3c76404b8d3d33358b92429a54 Mon Sep 17 00:00:00 2001 From: Cem Ibrahim ARI Date: Sun, 5 Jul 2020 09:03:50 +0000 Subject: [PATCH 029/167] fixed non valid json turkish language file --- lang/main-tr.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lang/main-tr.json b/lang/main-tr.json index fdf51db2a..506b03c0a 100644 --- a/lang/main-tr.json +++ b/lang/main-tr.json @@ -71,9 +71,8 @@ }, "privateNotice": "{{recipient}} için özel mesaj", "title": "Sohbet", - "you": "sen", - - + "you": "sen" + }, "connectingOverlay": { "joiningRoom": "Toplantıya bağlanılıyor..." From 29c16e42bd9553a5b32728c55a9cc4630e24b3e0 Mon Sep 17 00:00:00 2001 From: Frank de Lange Date: Tue, 7 Jul 2020 15:14:28 +0200 Subject: [PATCH 030/167] Move STUN/TURN to IANA-assigned ports - 3478 and 5349 (TLS) (#6172) * Move STUN/TURN to IANA-assigned ports - 3478 and 5349 (TLS) * Change remaining references to TURNS port from 4445 to 5349 * Change back TURNS to 443 --- config.js | 2 +- debian/jitsi-meet-turnserver.postinst | 4 ++-- doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example | 4 ++-- doc/debian/jitsi-meet-turn/turnserver.conf | 4 ++-- doc/debian/jitsi-meet/jitsi-meet.conf | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config.js b/config.js index 8841d9ec1..0e4bbad5b 100644 --- a/config.js +++ b/config.js @@ -361,7 +361,7 @@ var config = { // The STUN servers that will be used in the peer to peer connections stunServers: [ - // { urls: 'stun:jitsi-meet.example.com:4446' }, + // { urls: 'stun:jitsi-meet.example.com:3478' }, { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' } ] diff --git a/debian/jitsi-meet-turnserver.postinst b/debian/jitsi-meet-turnserver.postinst index 4272647c5..e3ca04752 100644 --- a/debian/jitsi-meet-turnserver.postinst +++ b/debian/jitsi-meet-turnserver.postinst @@ -49,7 +49,7 @@ case "$1" in # nothing to do echo "------------------------------------------------" echo "" - echo "turnserver is listening on tcp 4445 as other nginx sites use port 443" + echo "turnserver is listening on tcp 5349 as other nginx sites use port 443" echo "" echo "------------------------------------------------" NGINX_MULTIPLEXING="false" @@ -152,7 +152,7 @@ case "$1" in PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua" if [ -f $PROSODY_HOST_CONFIG ] ; then # If we are not multiplexing we need to change the port in prosody config - sed -i 's/"443"/"4445"/g' $PROSODY_HOST_CONFIG + sed -i 's/"443"/"5349"/g' $PROSODY_HOST_CONFIG invoke-rc.d prosody restart || true fi fi diff --git a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example index 3e8669f89..1e6163d88 100644 --- a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example +++ b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example @@ -6,8 +6,8 @@ muc_mapper_domain_base = "jitmeet.example.com"; turncredentials_secret = "__turnSecret__"; turncredentials = { - { type = "stun", host = "jitmeet.example.com", port = "4446" }, - { type = "turn", host = "jitmeet.example.com", port = "4446", transport = "udp" }, + { type = "stun", host = "jitmeet.example.com", port = "3478" }, + { type = "turn", host = "jitmeet.example.com", port = "3478", transport = "udp" }, { type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" } }; diff --git a/doc/debian/jitsi-meet-turn/turnserver.conf b/doc/debian/jitsi-meet-turn/turnserver.conf index f37c6db50..57ae23e35 100644 --- a/doc/debian/jitsi-meet-turn/turnserver.conf +++ b/doc/debian/jitsi-meet-turn/turnserver.conf @@ -10,8 +10,8 @@ no-cli no-loopback-peers no-tcp-relay no-tcp -listening-port=4446 -tls-listening-port=4445 +listening-port=3478 +tls-listening-port=5349 external-ip=__external_ip_address__ no-tlsv1 no-tlsv1_1 diff --git a/doc/debian/jitsi-meet/jitsi-meet.conf b/doc/debian/jitsi-meet/jitsi-meet.conf index 989d6a154..879fcf29e 100644 --- a/doc/debian/jitsi-meet/jitsi-meet.conf +++ b/doc/debian/jitsi-meet/jitsi-meet.conf @@ -7,7 +7,7 @@ stream { server 127.0.0.1:4444; } upstream turn { - server 127.0.0.1:4445; + server 127.0.0.1:5349; } # since 1.13.10 map $ssl_preread_alpn_protocols $upstream { From 1c122705bf2fd38f7a820352a2aa916cd03cc7cb Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 7 Jul 2020 15:40:18 +0200 Subject: [PATCH 031/167] Update main.json with missing KnockingParticipantList string (#7246) Adding future translation possibilities --- lang/main.json | 1 + 1 file changed, 1 insertion(+) diff --git a/lang/main.json b/lang/main.json index a552db864..e20ef5778 100644 --- a/lang/main.json +++ b/lang/main.json @@ -864,6 +864,7 @@ "header": "Help center" }, "lobby": { + "knockingParticipantList" : "Knocking participant list", "allow": "Allow", "backToKnockModeButton": "No password, ask to join instead", "dialogTitle": "Lobby mode", From e0b3a81a4129eb946264eb9c50e1409d849b8666 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 7 Jul 2020 10:40:10 +0200 Subject: [PATCH 032/167] Update main-fr.json Fix translate FR and add "Security" translations --- lang/main-fr.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lang/main-fr.json b/lang/main-fr.json index 706ab6314..80325498f 100644 --- a/lang/main-fr.json +++ b/lang/main-fr.json @@ -785,6 +785,7 @@ "header": "Centre d'aide" }, "lobby": { + "knockingParticipantList" : "Liste des participants en attente", "allow": "Autoriser", "backToKnockModeButton": "Aucun mot de passe, demander à rejoindre plutôt", "dialogTitle": "Mode lobby", @@ -793,7 +794,7 @@ "emailField": "Saisissez votre adresse email", "enableDialogPasswordField": "Définir le mot de passe (optionel)", "enableDialogSubmit": "Activer", - "enableDialogText": "Le mode lobby vous permet de protéger votre réunion en n'autorisant les personnes à entrer qu'après l'approbation formelle d'un modérateur.", + "enableDialogText": "Le mode lobby vous permet de protéger votre réunion en autorisant les personnes à entrer qu'après l'approbation formelle d'un modérateur.", "enterPasswordButton": "Saisissez un mot de passe de réunion", "enterPasswordTitle": "Saisissez le mot de passe pour rejoindre la réunion", "invalidPassword": "Mot de passe invalide", @@ -810,5 +811,11 @@ "passwordJoinButton": "Rejoindre", "reject": "Refuser", "toggleLabel": "Activer le lobby" + }, + "security": { + "about": "Vous pouvez ajouter un mot de passe à votre réunion. Les participants devront fournir le mot de passe avant qu'ils soient autorisés à rejoindre la réunion.", + "aboutReadOnly": "Les modérateurs peuvent ajouter un mot de passe à la réunion. Les participants devront fournir le mot de passe avant qu'ils soient autorisés à rejoindre la réunion.", + "insecureRoomNameWarning": "Le nom de la salle est peu sûr. Des participants non désirés peuvent rejoindre votre réunion. Pensez à sécuriser votre réunion en cliquant sur le bouton de sécurité.", + "securityOptions": "Options de sécurité" } } From bbf76296ed61f1c8b72e04612720006c23e3f13b Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 7 Jul 2020 15:35:50 +0200 Subject: [PATCH 033/167] Update KnockingParticipantList.js In order to translate the title.. Pull request #7246 --- react/features/lobby/components/web/KnockingParticipantList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/features/lobby/components/web/KnockingParticipantList.js b/react/features/lobby/components/web/KnockingParticipantList.js index f2ee61763..eaf3dfced 100644 --- a/react/features/lobby/components/web/KnockingParticipantList.js +++ b/react/features/lobby/components/web/KnockingParticipantList.js @@ -40,7 +40,7 @@ class KnockingParticipantList extends AbstractKnockingParticipantList { className = { _toolboxVisible ? 'toolbox-visible' : '' } id = 'knocking-participant-list'> - Knocking participant list + { t('lobby.knockingParticipantList') }
    { _participants.map(p => ( From bbcc40a97e0f281531ebabd9efefaf8094ef84c6 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 7 Jul 2020 17:35:52 +0200 Subject: [PATCH 034/167] Update main-fr.json Translate for "Add" in security option --- lang/main-fr.json | 1 + 1 file changed, 1 insertion(+) diff --git a/lang/main-fr.json b/lang/main-fr.json index 80325498f..969ef5f6c 100644 --- a/lang/main-fr.json +++ b/lang/main-fr.json @@ -147,6 +147,7 @@ "accessibilityLabel": { "liveStreaming": "Diffusion en direct" }, + "add": "Ajouter", "allow": "Autoriser", "alreadySharedVideoMsg": "Un autre participant est en train de partager sa vidéo. Cette conférence ne permet de partager qu'une seule vidéo à la fois.", "alreadySharedVideoTitle": "Une seule vidéo partagée est autorisée à la fois", From 1fff5d25673f09c0d3f902c6b3fe22ecd4990ebd Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 7 Jul 2020 17:45:38 +0200 Subject: [PATCH 035/167] Update main-fr.json Some translations for "invite" --- lang/main-fr.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lang/main-fr.json b/lang/main-fr.json index 969ef5f6c..48a6751d2 100644 --- a/lang/main-fr.json +++ b/lang/main-fr.json @@ -1,21 +1,36 @@ { "addPeople": { "add": "Inviter", + "addContacts": "Inviter vos contacts", + "copyInvite": "Copier l'invitation à la réunion", + "copyLink": "Copier le lien de la réunion", + "copyStream": "Copier le lien de diffision en direct", "countryNotSupported": "Cette destination n'est pas actuellement supportée.", "countryReminder": "Appel hors des États-Unis ? Veuillez débuter par le code du pays !", + "defaultEmail": "Votre email par défaut", "disabled": "Vous ne pouvez pas inviter quelqu'un.", "failedToAdd": "Erreur lors de l'ajout des participants", "footerText": "Appels sortants désactivés.", + "googleEmail": "Gmail", + "inviteMoreHeader": "Vous êtes seul(e) dans la réunion", + "inviteMoreMailSubject": "Rejoindre une réunion {{appName}}", + "inviteMorePrompt": "Inviter d'autres personnes", + "linkCopied": "Lien copié dans le presse-papier", "loading": "Rechercher des personnes et des numéros de téléphone", "loadingNumber": "Validation du numéro de téléphone", "loadingPeople": "Recherche de personnes à inviter", "noResults": "Aucun résultat de recherche correspondant", "noValidNumbers": "Veuillez entrer un numéro de téléphone", + "outlookEmail": "Outlook", "searchNumbers": "Ajouter des numéros de téléphone", "searchPeople": "Rechercher une personne", "searchPeopleAndNumbers": "Rechercher des personnes ou ajouter leurs numéros de téléphone", + "shareInvite": "Partager l'invitation à la réunion", + "shareLink": "Partager le lien de la réunion pour inviter d'autres personnes", + "shareStream": "Partager le lien de diffusion en direct", "telephone": "Téléphone : {{number}}", - "title": "Inviter une personne à cette réunion" + "title": "Inviter une personne à cette réunion", + "yahooEmail": "Yahoo" }, "audioDevices": { "bluetooth": "Bluetooth", From 4e1f42a665f9a5443936ddc49dfb455deaf31d22 Mon Sep 17 00:00:00 2001 From: Maxence Dalmais Date: Wed, 8 Jul 2020 10:09:39 +0200 Subject: [PATCH 036/167] Update mod_muc_poltergeist.lua Add avatar to user context so it is picked by the web interface --- resources/prosody-plugins/mod_muc_poltergeist.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/prosody-plugins/mod_muc_poltergeist.lua b/resources/prosody-plugins/mod_muc_poltergeist.lua index 0868b7bd4..fbb8fc1d2 100644 --- a/resources/prosody-plugins/mod_muc_poltergeist.lua +++ b/resources/prosody-plugins/mod_muc_poltergeist.lua @@ -221,7 +221,9 @@ function handle_create_poltergeist (event) creator_user = session.jitsi_meet_context_user; creator_group = session.jitsi_meet_context_group; }; - + if avatar ~= nil then + context.user.avatar = avatar + end local resources = {}; if conversation ~= nil then resources["conversation"] = conversation From 4a3cd2596a83852b8dea1385b05a78eaf41aba90 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 8 Jul 2020 08:01:47 +0200 Subject: [PATCH 037/167] Update main-fr.json Translation 'prejoin' --- lang/main-fr.json | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lang/main-fr.json b/lang/main-fr.json index 48a6751d2..451c39da3 100644 --- a/lang/main-fr.json +++ b/lang/main-fr.json @@ -15,7 +15,7 @@ "inviteMoreHeader": "Vous êtes seul(e) dans la réunion", "inviteMoreMailSubject": "Rejoindre une réunion {{appName}}", "inviteMorePrompt": "Inviter d'autres personnes", - "linkCopied": "Lien copié dans le presse-papier", + "linkCopied": "Lien copié dans le presse-papiers", "loading": "Rechercher des personnes et des numéros de téléphone", "loadingNumber": "Validation du numéro de téléphone", "loadingPeople": "Recherche de personnes à inviter", @@ -484,6 +484,40 @@ "passwordSetRemotely": "défini par un autre participant", "passwordDigitsOnly": "Jusqu'à {{number}} chiffres", "poweredby": "produit par", + "prejoin": { + "audioAndVideoError": "Erreur audio et video:", + "audioOnlyError": "Erreur audio:", + "audioTrackError": "N'a pas pu créer la piste audio.", + "callMe": "Appelez-moi", + "callMeAtNumber": "Appelez-moi à ce numéro:", + "configuringDevices": "Configuration des appareils...", + "connectedWithAudioQ": "Êtes-vous connecté avec le microphone?", + "copyAndShare": "Copier & partager le lien", + "dialInMeeting": "Participez à la réunion", + "dialInPin": "Participez à la réunion et saisir le code PIN:", + "dialing": "Numérotation", + "doNotShow": "Ne plus afficher ceci", + "errorDialOut": "Impossible de composer le numéro", + "errorDialOutDisconnected": "Impossible de composer le numéro. Déconnecté", + "errorDialOutFailed": "Impossible de composer le numéro. L'appel a échoué", + "errorDialOutStatus": "Erreur lors de l'obtention de l'état d'appel sortant", + "errorStatusCode": "Erreur de numérotation, code d'état: {{status}}", + "errorValidation": "La validation du numéro a échoué", + "iWantToDialIn": "Je veux me connecter", + "joinAudioByPhone": "Rejoindre avec l'audio du téléphone", + "joinMeeting": "Rejoindre la réunion", + "joinWithoutAudio": "Rejoignez sans microphone", + "initiated": "Appel lancé", + "linkCopied": "Lien copié dans le presse-papiers", + "lookGood": "Il semble que votre microphone fonctionne correctement", + "or": "ou", + "calling": "Appel", + "startWithPhone": "Commencez avec l'audio du téléphone", + "screenSharingError": "Erreur de partage d'écran:", + "videoOnlyError": "Erreur vidéo:", + "videoTrackError": "Impossible de créer une piste vidéo.", + "viewAllNumbers": "voir tous les numéros" + }, "presenceStatus": { "busy": "Occupé", "calling": "Appel...", From f30dd9d881850ea9688409f55427e1041cd7aa92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 8 Jul 2020 14:10:21 +0200 Subject: [PATCH 038/167] deps: react-native-webrtc@1.84.0 --- ios/Podfile.lock | 4 ++-- package-lock.json | 12 ++++++------ package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 93bfbb3dd..3074997bc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -293,7 +293,7 @@ PODS: - React - react-native-netinfo (4.1.5): - React - - react-native-webrtc (1.75.3): + - react-native-webrtc (1.84.0): - React - react-native-webview (7.4.1): - React @@ -569,7 +569,7 @@ SPEC CHECKSUMS: react-native-calendar-events: 1442fad71a00388f933cfa25512588fec300fcf8 react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a - react-native-webrtc: 86d841823e66d68cc1f86712db1c2956056bf0c2 + react-native-webrtc: 9268ae9a2bc9730796b0968d012327e92c392adf react-native-webview: 4dbc1d2a4a6b9c5e9e723c62651917aa2b5e579e React-RCTActionSheet: b72ddbfbe15b44ce691d128e4b582f4bb9abb540 React-RCTAnimation: cfaefba5024499d336b76ab850e6bd33b232b5e3 diff --git a/package-lock.json b/package-lock.json index 585bf35d9..98d5fbd53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14319,9 +14319,9 @@ "integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow==" }, "react-native-webrtc": { - "version": "1.75.3", - "resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.75.3.tgz", - "integrity": "sha512-WIQu7jpH4osb4IMdjkpr62UYwt9kHIU4vWvvTHMxSqkGZwMGpTyFW/E+Y0T7If1azAvhq3GBuQjI3npxIXGbSQ==", + "version": "1.84.0", + "resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.84.0.tgz", + "integrity": "sha512-xPOFbrcehuBzLnFy3keCM2HyMsyCVDQjQNAn8SIHKH/PA8Q7kZ4spuytc2E1hBTr7zH/vQ2Px+DWqu7on12jag==", "requires": { "base64-js": "^1.1.2", "event-target-shim": "^1.0.5", @@ -14330,9 +14330,9 @@ }, "dependencies": { "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, diff --git a/package.json b/package.json index ca28af0fd..11a103443 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "react-native-svg-transformer": "0.13.0", "react-native-swipeout": "2.3.6", "react-native-watch-connectivity": "0.4.3", - "react-native-webrtc": "1.75.3", + "react-native-webrtc": "1.84.0", "react-native-webview": "7.4.1", "react-native-youtube-iframe": "1.2.3", "react-redux": "7.1.0", From f22d5ed629cd8bf2a9cca800300cc2bb921bad86 Mon Sep 17 00:00:00 2001 From: motiwardi <67767494+motiwardi@users.noreply.github.com> Date: Wed, 8 Jul 2020 05:50:56 -0700 Subject: [PATCH 039/167] android: added configuration for user CA root trust --- .../src/main/res/xml/network_security_config.xml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/res/xml/network_security_config.xml b/android/app/src/main/res/xml/network_security_config.xml index 59a524cdc..cfdf6a502 100644 --- a/android/app/src/main/res/xml/network_security_config.xml +++ b/android/app/src/main/res/xml/network_security_config.xml @@ -1,6 +1,12 @@ - - - localhost - 10.0.2.2 - + + + + + + + + + localhost + 10.0.2.2 + From 3538761543c7207c1a251e1d989c935b2b2ba24f Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Wed, 8 Jul 2020 12:59:46 +0300 Subject: [PATCH 040/167] fix(prejoin): Don't add video track to connection on start if video muted --- react/features/prejoin/actions.js | 18 +++--------------- react/features/prejoin/middleware.js | 13 +++++++++++-- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/react/features/prejoin/actions.js b/react/features/prejoin/actions.js index 34a9d0ef1..a43075905 100644 --- a/react/features/prejoin/actions.js +++ b/react/features/prejoin/actions.js @@ -199,14 +199,13 @@ export function initPrejoin(tracks: Object[], errors: Object) { } /** - * Joins the conference. + * Action used to start the conference. * * @returns {Function} */ export function joinConference() { - return function(dispatch: Function) { - dispatch(setPrejoinPageVisibility(false)); - dispatch(startConference()); + return { + type: PREJOIN_START_CONFERENCE }; } @@ -418,14 +417,3 @@ export function setPrejoinPageVisibility(value: boolean) { value }; } - -/** - * Action used to mark the start of the conference. - * - * @returns {Object} - */ -function startConference() { - return { - type: PREJOIN_START_CONFERENCE - }; -} diff --git a/react/features/prejoin/middleware.js b/react/features/prejoin/middleware.js index 2b983b15e..de3363c40 100644 --- a/react/features/prejoin/middleware.js +++ b/react/features/prejoin/middleware.js @@ -2,8 +2,10 @@ import { MiddlewareRegistry } from '../base/redux'; import { updateSettings } from '../base/settings'; +import { getLocalVideoTrack, replaceLocalTrack } from '../base/tracks'; import { PREJOIN_START_CONFERENCE } from './actionTypes'; +import { setPrejoinPageVisibility } from './actions'; declare var APP: Object; @@ -19,13 +21,20 @@ MiddlewareRegistry.register(store => next => async action => { const { getState, dispatch } = store; const state = getState(); const { userSelectedSkipPrejoin } = state['features/prejoin']; - const tracks = state['features/base/tracks']; + const localVideoTrack = getLocalVideoTrack(state['features/base/tracks']); userSelectedSkipPrejoin && dispatch(updateSettings({ userSelectedSkipPrejoin })); - APP.conference.prejoinStart(tracks.map(t => t.jitsiTrack)); + if (localVideoTrack?.muted) { + await dispatch(replaceLocalTrack(localVideoTrack.jitsiTrack, null)); + } + + const jitsiTracks = getState()['features/base/tracks'].map(t => t.jitsiTrack); + + dispatch(setPrejoinPageVisibility(false)); + APP.conference.prejoinStart(jitsiTracks); break; } From a61f27230382bf3270ed9ac9eca107f2a6d23eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 8 Jul 2020 14:54:24 +0200 Subject: [PATCH 041/167] deps: lib-jitsi-meet@latest --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98d5fbd53..1dd026729 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10724,8 +10724,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#e66cc365014cd429280a95a379ad62d993217f6b", - "from": "github:jitsi/lib-jitsi-meet#e66cc365014cd429280a95a379ad62d993217f6b", + "version": "github:jitsi/lib-jitsi-meet#6c04e2739ad4513cddc3eba98dbe4e651b4a8fe5", + "from": "github:jitsi/lib-jitsi-meet#6c04e2739ad4513cddc3eba98dbe4e651b4a8fe5", "requires": { "@jitsi/sdp-interop": "1.0.3", "@jitsi/sdp-simulcast": "0.3.0", diff --git a/package.json b/package.json index 11a103443..af65adbaa 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "js-md5": "0.6.1", "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#e66cc365014cd429280a95a379ad62d993217f6b", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#6c04e2739ad4513cddc3eba98dbe4e651b4a8fe5", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", From 95825dcdd7714d434c4a8bf109c1b18d0b26849e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 3 Jul 2020 12:36:51 +0200 Subject: [PATCH 042/167] config: add flag to disable the E2EE support This is useful for testing insertable streams related issues. --- config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config.js b/config.js index 0e4bbad5b..ebc4a1c9c 100644 --- a/config.js +++ b/config.js @@ -44,6 +44,10 @@ var config = { // testing: { + // Disables the End to End Encryption feature. Useful for debugging + // issues related to insertable streams. + // disableE2EE: false, + // P2P test mode disables automatic switching to P2P when there are 2 // participants in the conference. p2pTestMode: false From 8d1bde3cb137313fd737354db0e9acbe7757716a Mon Sep 17 00:00:00 2001 From: Jaya Allamsetty Date: Wed, 8 Jul 2020 14:17:26 -0400 Subject: [PATCH 043/167] chore(deps): update lib-jitsi-meet to latest --- package-lock.json | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1dd026729..a59356255 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10724,8 +10724,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#6c04e2739ad4513cddc3eba98dbe4e651b4a8fe5", - "from": "github:jitsi/lib-jitsi-meet#6c04e2739ad4513cddc3eba98dbe4e651b4a8fe5", + "version": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", + "from": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", "requires": { "@jitsi/sdp-interop": "1.0.3", "@jitsi/sdp-simulcast": "0.3.0", @@ -10733,6 +10733,7 @@ "current-executing-script": "0.1.3", "jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b", "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", + "lodash.clonedeep": "4.5.0", "lodash.isequal": "4.5.0", "sdp-transform": "2.3.0", "strophe.js": "1.3.4", diff --git a/package.json b/package.json index af65adbaa..816c36b09 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "js-md5": "0.6.1", "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#6c04e2739ad4513cddc3eba98dbe4e651b4a8fe5", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", From 7d513738d2179b68a8b21233d2bc9e93d3da05ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 7 Jul 2020 22:34:52 +0200 Subject: [PATCH 044/167] rn,flags: add ability to override resolution using a flag Also, use the configured resolution to set it as the max received frame size. --- react/features/app/middlewares.any.js | 1 + react/features/base/conference/middleware.js | 5 ++- react/features/base/config/middleware.js | 7 +++++ react/features/base/flags/constants.js | 7 +++++ react/features/conference/middleware.js | 4 +-- react/features/video-layout/subscriber.js | 3 +- react/features/video-quality/middleware.js | 33 ++++++++++++++++++++ 7 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 react/features/video-quality/middleware.js diff --git a/react/features/app/middlewares.any.js b/react/features/app/middlewares.any.js index 019cae38e..ec46cb67a 100644 --- a/react/features/app/middlewares.any.js +++ b/react/features/app/middlewares.any.js @@ -41,6 +41,7 @@ import '../subtitles/middleware'; import '../toolbox/middleware'; import '../transcribing/middleware'; import '../video-layout/middleware'; +import '../video-quality/middleware'; import '../videosipgw/middleware'; import './middleware'; diff --git a/react/features/base/conference/middleware.js b/react/features/base/conference/middleware.js index 130729bea..a5b0c233f 100644 --- a/react/features/base/conference/middleware.js +++ b/react/features/base/conference/middleware.js @@ -460,7 +460,10 @@ function _sendTones({ getState }, next, action) { */ function _setReceiverVideoConstraint(conference, preferred, max) { if (conference) { - conference.setReceiverVideoConstraint(Math.min(preferred, max)); + const value = Math.min(preferred, max); + + conference.setReceiverVideoConstraint(value); + logger.info(`setReceiverVideoConstraint: ${value}`); } } diff --git a/react/features/base/config/middleware.js b/react/features/base/config/middleware.js index 96c2954a6..4fb82b3b3 100644 --- a/react/features/base/config/middleware.js +++ b/react/features/base/config/middleware.js @@ -3,6 +3,7 @@ import { jitsiLocalStorage } from 'js-utils'; import { APP_WILL_MOUNT } from '../app'; +import { getFeatureFlag } from '../flags/functions'; import { addKnownDomains } from '../known-domains'; import { MiddlewareRegistry } from '../redux'; import { parseURIString } from '../util'; @@ -107,6 +108,12 @@ function _setConfig({ dispatch, getState }, next, action) { config.p2p = { enabled: !settings.disableP2P }; } + const resolutionFlag = getFeatureFlag(state, 'resolution'); + + if (typeof resolutionFlag !== 'undefined') { + config.resolution = resolutionFlag; + } + dispatch({ type: _UPDATE_CONFIG, config diff --git a/react/features/base/flags/constants.js b/react/features/base/flags/constants.js index bc94c382d..7592cc901 100644 --- a/react/features/base/flags/constants.js +++ b/react/features/base/flags/constants.js @@ -81,6 +81,13 @@ export const RAISE_HAND_ENABLED = 'raise-hand.enabled'; */ export const RECORDING_ENABLED = 'recording.enabled'; +/** + * Flag indicating the local and (maximum) remote video resolution. Overrides + * the server configuration. + * Default: (unset). + */ +export const RESOLUTION = 'resolution'; + /** * Flag indicating if server URL change is enabled. * Default: enabled (true) diff --git a/react/features/conference/middleware.js b/react/features/conference/middleware.js index cf8fcf42f..6348e8d32 100644 --- a/react/features/conference/middleware.js +++ b/react/features/conference/middleware.js @@ -6,7 +6,7 @@ import { VIDEO_QUALITY_LEVELS, conferenceLeft, getCurrentConference, - setPreferredVideoQuality + setMaxReceiverVideoQuality } from '../base/conference'; import { hideDialog, isDialogOpen } from '../base/dialog'; import { setActiveModalId } from '../base/modal'; @@ -33,7 +33,7 @@ MiddlewareRegistry.register(store => next => action => { dispatch(setFilmstripEnabled(!reducedUI)); dispatch( - setPreferredVideoQuality( + setMaxReceiverVideoQuality( reducedUI ? VIDEO_QUALITY_LEVELS.LOW : VIDEO_QUALITY_LEVELS.HIGH)); diff --git a/react/features/video-layout/subscriber.js b/react/features/video-layout/subscriber.js index 17a74dfa5..fb76d35ea 100644 --- a/react/features/video-layout/subscriber.js +++ b/react/features/video-layout/subscriber.js @@ -32,8 +32,7 @@ StateListenerRegistry.register( dispatch(selectParticipant()); if (!displayTileView) { - dispatch( - setMaxReceiverVideoQuality(VIDEO_QUALITY_LEVELS.HIGH)); + dispatch(setMaxReceiverVideoQuality(VIDEO_QUALITY_LEVELS.HIGH)); if (_getAutoPinSetting()) { _updateAutoPinnedParticipant(store); diff --git a/react/features/video-quality/middleware.js b/react/features/video-quality/middleware.js new file mode 100644 index 000000000..227178dc0 --- /dev/null +++ b/react/features/video-quality/middleware.js @@ -0,0 +1,33 @@ +// @flow + +import { CONFERENCE_JOINED } from '../base/conference/actionTypes'; +import { setPreferredVideoQuality } from '../base/conference/actions'; +import { MiddlewareRegistry } from '../base/redux'; + +import logger from './logger'; + +/** + * Implements the middleware of the feature video-quality. + * + * @param {Store} store - The redux store. + * @returns {Function} + */ +MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { + const result = next(action); + + switch (action.type) { + case CONFERENCE_JOINED: { + if (navigator.product === 'ReactNative') { + const { resolution } = getState()['features/base/config']; + + if (typeof resolution !== 'undefined') { + dispatch(setPreferredVideoQuality(Number.parseInt(resolution, 10))); + logger.info(`Configured preferred receiver video frame height to: ${resolution}`); + } + } + break; + } + } + + return result; +}); From f32140c4b7f4bc9654067cb68882118bbdfae952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 7 Jul 2020 22:35:39 +0200 Subject: [PATCH 045/167] rn: set default resolution to 360p (experiment) --- android/app/src/main/java/org/jitsi/meet/MainActivity.java | 1 + ios/app/src/AppDelegate.m | 1 + 2 files changed, 2 insertions(+) diff --git a/android/app/src/main/java/org/jitsi/meet/MainActivity.java b/android/app/src/main/java/org/jitsi/meet/MainActivity.java index d420016cf..275687d33 100644 --- a/android/app/src/main/java/org/jitsi/meet/MainActivity.java +++ b/android/app/src/main/java/org/jitsi/meet/MainActivity.java @@ -147,6 +147,7 @@ public class MainActivity extends JitsiMeetActivity { .setWelcomePageEnabled(true) .setServerURL(buildURL(defaultURL)) .setFeatureFlag("call-integration.enabled", false) + .setFeatureFlag("resolution", 360) .setFeatureFlag("server-url-change.enabled", !configurationByRestrictions) .build(); JitsiMeet.setDefaultConferenceOptions(defaultOptions); diff --git a/ios/app/src/AppDelegate.m b/ios/app/src/AppDelegate.m index 27025675d..3a4395a7d 100644 --- a/ios/app/src/AppDelegate.m +++ b/ios/app/src/AppDelegate.m @@ -36,6 +36,7 @@ jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"alpha.jitsi.net", @"beta.meet.jit.si"]; jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) { + [builder setFeatureFlag:@"resolution" withValue:@(360)]; builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"]; builder.welcomePageEnabled = YES; From ce812591f9f628f330d74862b0e6d1d73f9508ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 8 Jul 2020 10:27:18 +0200 Subject: [PATCH 046/167] conference: fix not applying max recv constraints They also need to be applied when changing conferences. --- react/features/base/conference/middleware.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/react/features/base/conference/middleware.js b/react/features/base/conference/middleware.js index a5b0c233f..9b52dd4e9 100644 --- a/react/features/base/conference/middleware.js +++ b/react/features/base/conference/middleware.js @@ -117,14 +117,15 @@ StateListenerRegistry.register( maxReceiverVideoQuality, preferredVideoQuality } = currentState; + const changedConference = conference !== previousState.conference; const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality; const changedMaxVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality; - if (changedPreferredVideoQuality || changedMaxVideoQuality) { + if (changedConference || changedPreferredVideoQuality || changedMaxVideoQuality) { _setReceiverVideoConstraint(conference, preferredVideoQuality, maxReceiverVideoQuality); } - if (changedPreferredVideoQuality) { + if (changedConference || changedPreferredVideoQuality) { _setSenderVideoConstraint(conference, preferredVideoQuality); } }); From b91d6b97a96872f9785f920a9b8d67ecca27a83c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Thu, 9 Jul 2020 09:17:23 +0200 Subject: [PATCH 047/167] deps: jitsi/js-utils@1.0.0 --- connection.js | 2 +- modules/UI/util/MessageHandler.js | 2 +- modules/transport/index.js | 2 +- package-lock.json | 17 +++++++++++++++++ package.json | 2 +- react/features/app/getRouteToRender.js | 2 +- react/features/base/app/components/BaseApp.js | 2 +- react/features/base/config/actions.js | 2 +- react/features/base/config/functions.any.js | 2 +- react/features/base/config/middleware.js | 2 +- react/features/base/participants/functions.js | 3 ++- .../features/base/redux/PersistenceRegistry.js | 2 +- react/features/base/settings/reducer.js | 4 ++-- react/features/calendar-sync/actions.native.js | 2 +- react/features/calendar-sync/actions.web.js | 2 +- .../components/ChromeExtensionBanner.web.js | 2 +- .../local-recording/session/SessionManager.js | 2 +- .../components/AbstractPageReloadOverlay.js | 2 +- react/features/recent-list/reducer.js | 2 +- .../welcome/components/AbstractWelcomePage.js | 2 +- webpack.config.js | 2 +- 21 files changed, 39 insertions(+), 21 deletions(-) diff --git a/connection.js b/connection.js index 1f8b43117..dbae44694 100644 --- a/connection.js +++ b/connection.js @@ -1,7 +1,7 @@ /* global APP, JitsiMeetJS, config */ +import { jitsiLocalStorage } from '@jitsi/js-utils'; import Logger from 'jitsi-meet-logger'; -import { jitsiLocalStorage } from 'js-utils'; import AuthHandler from './modules/UI/authentication/AuthHandler'; import { diff --git a/modules/UI/util/MessageHandler.js b/modules/UI/util/MessageHandler.js index f9f9cb3cf..1f446420a 100644 --- a/modules/UI/util/MessageHandler.js +++ b/modules/UI/util/MessageHandler.js @@ -1,7 +1,7 @@ /* global $, APP */ +import { jitsiLocalStorage } from '@jitsi/js-utils'; import Logger from 'jitsi-meet-logger'; -import { jitsiLocalStorage } from 'js-utils'; import { NOTIFICATION_TIMEOUT, diff --git a/modules/transport/index.js b/modules/transport/index.js index 2af297f41..e79d459c1 100644 --- a/modules/transport/index.js +++ b/modules/transport/index.js @@ -1,6 +1,6 @@ // FIXME: change to '../API' when we update to webpack2. If we do this now all // files from API modules will be included in external_api.js. -import { PostMessageTransportBackend, Transport } from 'js-utils/transport'; +import { PostMessageTransportBackend, Transport } from '@jitsi/js-utils/transport'; import { getJitsiMeetGlobalNS } from '../../react/features/base/util'; import { API_ID } from '../API/constants'; diff --git a/package-lock.json b/package-lock.json index a59356255..f2c6a450e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2832,6 +2832,23 @@ "@types/yargs": "^13.0.0" } }, + "@jitsi/js-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.0.tgz", + "integrity": "sha512-at9GPMP7IL0v6QS1Gs9c5MbbiN2AT0uKzsgKM8qS2wqXxqvpfT3p4U3+LH2IUyXiHlkmvlBMcNM02MltBdyRmQ==", + "requires": { + "bowser": "2.7.0", + "js-md5": "0.7.3", + "postis": "2.2.0" + }, + "dependencies": { + "js-md5": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz", + "integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==" + } + } + }, "@jitsi/sdp-interop": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@jitsi/sdp-interop/-/sdp-interop-1.0.3.tgz", diff --git a/package.json b/package.json index 816c36b09..4d367aa58 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@atlaskit/theme": "7.0.2", "@atlaskit/toggle": "5.0.14", "@atlaskit/tooltip": "12.1.13", + "@jitsi/js-utils": "1.0.0", "@microsoft/microsoft-graph-client": "1.1.0", "@react-native-community/async-storage": "1.3.4", "@react-native-community/google-signin": "3.0.1", @@ -54,7 +55,6 @@ "jquery-contextmenu": "2.4.5", "jquery-i18next": "1.2.1", "js-md5": "0.6.1", - "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", "jwt-decode": "2.2.0", "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", diff --git a/react/features/app/getRouteToRender.js b/react/features/app/getRouteToRender.js index a4bcdd5f4..1ff70a906 100644 --- a/react/features/app/getRouteToRender.js +++ b/react/features/app/getRouteToRender.js @@ -1,6 +1,6 @@ // @flow -import { generateRoomWithoutSeparator } from 'js-utils/random'; +import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random'; import type { Component } from 'react'; import { isRoomValid } from '../base/conference'; diff --git a/react/features/base/app/components/BaseApp.js b/react/features/base/app/components/BaseApp.js index e01a091df..c6af96180 100644 --- a/react/features/base/app/components/BaseApp.js +++ b/react/features/base/app/components/BaseApp.js @@ -1,6 +1,6 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; import { I18nextProvider } from 'react-i18next'; diff --git a/react/features/base/config/actions.js b/react/features/base/config/actions.js index 0c5f930ab..d890ecbed 100644 --- a/react/features/base/config/actions.js +++ b/react/features/base/config/actions.js @@ -1,6 +1,6 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import type { Dispatch } from 'redux'; import { addKnownDomains } from '../known-domains'; diff --git a/react/features/base/config/functions.any.js b/react/features/base/config/functions.any.js index ff5a448f9..1db8102a5 100644 --- a/react/features/base/config/functions.any.js +++ b/react/features/base/config/functions.any.js @@ -1,6 +1,6 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import _ from 'lodash'; import { parseURLParams } from '../util'; diff --git a/react/features/base/config/middleware.js b/react/features/base/config/middleware.js index 4fb82b3b3..2a86a8240 100644 --- a/react/features/base/config/middleware.js +++ b/react/features/base/config/middleware.js @@ -1,6 +1,6 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import { APP_WILL_MOUNT } from '../app'; import { getFeatureFlag } from '../flags/functions'; diff --git a/react/features/base/participants/functions.js b/react/features/base/participants/functions.js index c554e7228..afb1aa78a 100644 --- a/react/features/base/participants/functions.js +++ b/react/features/base/participants/functions.js @@ -1,5 +1,6 @@ // @flow -import { getGravatarURL } from 'js-utils/avatar'; + +import { getGravatarURL } from '@jitsi/js-utils/avatar'; import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet'; import { MEDIA_TYPE, shouldRenderVideoTrack } from '../media'; diff --git a/react/features/base/redux/PersistenceRegistry.js b/react/features/base/redux/PersistenceRegistry.js index 441a688dd..34313172d 100644 --- a/react/features/base/redux/PersistenceRegistry.js +++ b/react/features/base/redux/PersistenceRegistry.js @@ -1,7 +1,7 @@ // @flow +import { jitsiLocalStorage } from '@jitsi/js-utils'; import md5 from 'js-md5'; -import { jitsiLocalStorage } from 'js-utils'; import logger from './logger'; diff --git a/react/features/base/settings/reducer.js b/react/features/base/settings/reducer.js index 9e81470e5..a52412e5b 100644 --- a/react/features/base/settings/reducer.js +++ b/react/features/base/settings/reducer.js @@ -1,7 +1,7 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; -import { randomHexString } from 'js-utils/random'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; +import { randomHexString } from '@jitsi/js-utils/random'; import _ from 'lodash'; import { APP_WILL_MOUNT } from '../app/actionTypes'; diff --git a/react/features/calendar-sync/actions.native.js b/react/features/calendar-sync/actions.native.js index 344a8d4ec..1ea63aae8 100644 --- a/react/features/calendar-sync/actions.native.js +++ b/react/features/calendar-sync/actions.native.js @@ -1,5 +1,5 @@ // @flow -import { generateRoomWithoutSeparator } from 'js-utils/random'; +import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random'; import type { Dispatch } from 'redux'; import { getDefaultURL } from '../app/functions'; diff --git a/react/features/calendar-sync/actions.web.js b/react/features/calendar-sync/actions.web.js index 3c9189759..ad3a4e3e8 100644 --- a/react/features/calendar-sync/actions.web.js +++ b/react/features/calendar-sync/actions.web.js @@ -1,6 +1,6 @@ // @flow -import { generateRoomWithoutSeparator } from 'js-utils/random'; +import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random'; import type { Dispatch } from 'redux'; import { createCalendarConnectedEvent, sendAnalytics } from '../analytics'; diff --git a/react/features/chrome-extension-banner/components/ChromeExtensionBanner.web.js b/react/features/chrome-extension-banner/components/ChromeExtensionBanner.web.js index f54d3f721..5abf91450 100644 --- a/react/features/chrome-extension-banner/components/ChromeExtensionBanner.web.js +++ b/react/features/chrome-extension-banner/components/ChromeExtensionBanner.web.js @@ -1,6 +1,6 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import React, { PureComponent } from 'react'; import { diff --git a/react/features/local-recording/session/SessionManager.js b/react/features/local-recording/session/SessionManager.js index f8c13402a..794da8056 100644 --- a/react/features/local-recording/session/SessionManager.js +++ b/react/features/local-recording/session/SessionManager.js @@ -1,6 +1,6 @@ /* @flow */ -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import logger from '../logger'; diff --git a/react/features/overlay/components/AbstractPageReloadOverlay.js b/react/features/overlay/components/AbstractPageReloadOverlay.js index 139a4900e..68384468e 100644 --- a/react/features/overlay/components/AbstractPageReloadOverlay.js +++ b/react/features/overlay/components/AbstractPageReloadOverlay.js @@ -1,6 +1,6 @@ // @flow -import { randomInt } from 'js-utils/random'; +import { randomInt } from '@jitsi/js-utils/random'; import React, { Component } from 'react'; import type { Dispatch } from 'redux'; diff --git a/react/features/recent-list/reducer.js b/react/features/recent-list/reducer.js index 3a2bb9f21..62564f11c 100644 --- a/react/features/recent-list/reducer.js +++ b/react/features/recent-list/reducer.js @@ -1,6 +1,6 @@ // @flow -import { jitsiLocalStorage } from 'js-utils'; +import { jitsiLocalStorage } from '@jitsi/js-utils'; import { APP_WILL_MOUNT } from '../base/app'; import { getURLWithoutParamsNormalized } from '../base/connection'; diff --git a/react/features/welcome/components/AbstractWelcomePage.js b/react/features/welcome/components/AbstractWelcomePage.js index 7affe0dd1..13a9cd7d4 100644 --- a/react/features/welcome/components/AbstractWelcomePage.js +++ b/react/features/welcome/components/AbstractWelcomePage.js @@ -1,6 +1,6 @@ // @flow -import { generateRoomWithoutSeparator } from 'js-utils/random'; +import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random'; import { Component } from 'react'; import type { Dispatch } from 'redux'; diff --git a/webpack.config.js b/webpack.config.js index 49719fc97..7674fdf53 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,7 +55,7 @@ const config = { // as well. exclude: [ - new RegExp(`${__dirname}/node_modules/(?!js-utils)`) + new RegExp(`${__dirname}/node_modules/(?!@jitsi/js-utils)`) ], loader: 'babel-loader', options: { From 5bc3128c71145706ce722159f34783ef95e3f390 Mon Sep 17 00:00:00 2001 From: ALAGBE Sola Date: Fri, 10 Jul 2020 10:10:19 +0100 Subject: [PATCH 048/167] config: fix typo --- config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.js b/config.js index ebc4a1c9c..c3b42b5b4 100644 --- a/config.js +++ b/config.js @@ -310,7 +310,7 @@ var config = { // When 'true', it shows an intermediate page before joining, where the user can configure their devices. // prejoinPageEnabled: false, - // If true, shows the unsafe roon name warning label when a room name is + // If true, shows the unsafe room name warning label when a room name is // deemed unsafe (due to the simplicity in the name) and a password is not // set or the lobby is not enabled. // enableInsecureRoomNameWarning: false, From 62ad7d3451db10115d57b3c189703b43a447ab6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 10 Jul 2020 10:52:56 +0200 Subject: [PATCH 049/167] deps: lib-jitsi-meet@latest --- package-lock.json | 22 +++------------------- package.json | 2 +- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2c6a450e..44e941b56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10555,22 +10555,6 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, - "js-utils": { - "version": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", - "from": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", - "requires": { - "bowser": "2.7.0", - "js-md5": "0.7.3", - "postis": "2.2.0" - }, - "dependencies": { - "js-md5": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz", - "integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==" - } - } - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -10741,15 +10725,15 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", - "from": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", + "version": "github:jitsi/lib-jitsi-meet#cd008d726f1f57562eb5d8e6a3cd91c7e69826a0", + "from": "github:jitsi/lib-jitsi-meet#cd008d726f1f57562eb5d8e6a3cd91c7e69826a0", "requires": { + "@jitsi/js-utils": "1.0.0", "@jitsi/sdp-interop": "1.0.3", "@jitsi/sdp-simulcast": "0.3.0", "async": "0.9.0", "current-executing-script": "0.1.3", "jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b", - "js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4", "lodash.clonedeep": "4.5.0", "lodash.isequal": "4.5.0", "sdp-transform": "2.3.0", diff --git a/package.json b/package.json index 4d367aa58..68f51a385 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "jquery-i18next": "1.2.1", "js-md5": "0.6.1", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#fb997564a64ecdc19a35f3bcc1ea4156ca9ccb5f", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#cd008d726f1f57562eb5d8e6a3cd91c7e69826a0", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", From 546b0abe3274b6294facd4e932c81f23a80c43ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 10 Jul 2020 11:07:40 +0200 Subject: [PATCH 050/167] misc: add script to update LJM to the latest commit --- resources/update-ljm.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 resources/update-ljm.sh diff --git a/resources/update-ljm.sh b/resources/update-ljm.sh new file mode 100755 index 000000000..ec55cbdad --- /dev/null +++ b/resources/update-ljm.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e -u + +THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd) +LATEST_LJM_COMMIT=$(git ls-remote https://github.com/jitsi/lib-jitsi-meet.git HEAD | awk '{ print $1 }') + +pushd ${THIS_DIR}/.. + +npm install github:jitsi/lib-jitsi-meet#${LATEST_LJM_COMMIT} +git add package.json package-lock.json +git commit -m "deps: lib-jitsi-meet@latest" + +popd + +echo "Done! Now push your branch to GH and open a PR!" From bd65108692cec59d212f8cc4dfa00735f74528d9 Mon Sep 17 00:00:00 2001 From: Mihai Uscat Date: Fri, 10 Jul 2020 13:41:17 +0300 Subject: [PATCH 051/167] fix(SecurityDialog): Fix password action spacing --- css/modals/security/_security.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/css/modals/security/_security.scss b/css/modals/security/_security.scss index 50d3eb321..cc8ea66c0 100644 --- a/css/modals/security/_security.scss +++ b/css/modals/security/_security.scss @@ -25,6 +25,10 @@ font-size: 14px; color: #6FB1EA; } + + & > :first-child:not(:last-child) { + margin-right: 24px; + } } } } From f3765424417b4bfc9ae7a73c2025ea9ace50975e Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Mon, 29 Jun 2020 10:45:28 +0300 Subject: [PATCH 052/167] feat(prejoin) cache media start options when on prejoin screen --- react/features/prejoin/middleware.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/react/features/prejoin/middleware.js b/react/features/prejoin/middleware.js index de3363c40..611bea577 100644 --- a/react/features/prejoin/middleware.js +++ b/react/features/prejoin/middleware.js @@ -1,11 +1,13 @@ // @flow +import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media'; import { MiddlewareRegistry } from '../base/redux'; import { updateSettings } from '../base/settings'; import { getLocalVideoTrack, replaceLocalTrack } from '../base/tracks'; import { PREJOIN_START_CONFERENCE } from './actionTypes'; import { setPrejoinPageVisibility } from './actions'; +import { isPrejoinPageVisible } from './functions'; declare var APP: Object; @@ -38,8 +40,26 @@ MiddlewareRegistry.register(store => next => async action => { break; } + + case SET_AUDIO_MUTED: { + if (isPrejoinPageVisible(store.getState())) { + store.dispatch(updateSettings({ + startWithAudioMuted: Boolean(action.muted) + })); + } + break; } + case SET_VIDEO_MUTED: { + if (isPrejoinPageVisible(store.getState())) { + store.dispatch(updateSettings({ + startWithVideoMuted: Boolean(action.muted) + })); + } + break; + } + + } return next(action); }); From 0e5091adba3089c0b8e35e473b0a19f8ee36982e Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Mon, 29 Jun 2020 10:45:58 +0300 Subject: [PATCH 053/167] feat(prejoin) Expose prejoin app --- css/_premeeting-screens.scss | 24 ++-- index.html | 10 ++ react/features/base/app/components/BaseApp.js | 4 +- .../components/web/PreMeetingScreen.js | 36 ++++- .../base/premeeting/components/web/Preview.js | 37 ++++-- react/features/base/tracks/functions.js | 85 +++++++++++- react/features/base/tracks/middleware.js | 4 + react/features/prejoin/components/Prejoin.js | 123 +++++++++++------- .../features/prejoin/components/PrejoinApp.js | 93 +++++++++++++ react/index.web.js | 23 +++- static/prejoin.html | 41 ++++++ 11 files changed, 400 insertions(+), 80 deletions(-) create mode 100644 react/features/prejoin/components/PrejoinApp.js create mode 100644 static/prejoin.html diff --git a/css/_premeeting-screens.scss b/css/_premeeting-screens.scss index de1f76afa..7f490b867 100644 --- a/css/_premeeting-screens.scss +++ b/css/_premeeting-screens.scss @@ -1,17 +1,21 @@ /** * Shared style for full screen local track based dialogs/modals. */ -.premeeting-screen { + .premeeting-screen, + .preview-overlay { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + } + + .premeeting-screen { align-items: stretch; - background: #1C2025; - bottom: 0; + background: radial-gradient(50% 50% at 50% 50%, #5D95C7 0%, #376288 100%), #FFFFFF; display: flex; flex-direction: column; font-size: 1.3em; - left: 0; - position: absolute; - right: 0; - top: 0; z-index: $toolbarZ + 1; .action-btn { @@ -74,9 +78,13 @@ } } + .preview-overlay { + background-image: linear-gradient(transparent, black); + z-index: $toolbarZ + 1; + } + .content { align-items: center; - background-image: linear-gradient(transparent, black); display: flex; flex: 1; flex-direction: column; diff --git a/index.html b/index.html index 30cea926e..2a222ae99 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,17 @@ + + + + + + + + +
    + + From 3da1b65757502069b4dc8b1420ddfa35dac71156 Mon Sep 17 00:00:00 2001 From: damencho Date: Fri, 10 Jul 2020 07:10:37 -0500 Subject: [PATCH 054/167] fix: Fixes import of moved function. --- .../security/components/security-dialog/PasswordSection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/features/security/components/security-dialog/PasswordSection.js b/react/features/security/components/security-dialog/PasswordSection.js index 36b34e1d2..29ffd67fb 100644 --- a/react/features/security/components/security-dialog/PasswordSection.js +++ b/react/features/security/components/security-dialog/PasswordSection.js @@ -4,7 +4,7 @@ import React, { useRef } from 'react'; import { translate } from '../../../base/i18n'; -import { copyText } from '../../../invite'; +import { copyText } from '../../../base/util'; import PasswordForm from './PasswordForm'; From 53281c2d42a36d920ce09e029d2da445119375fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 10 Jul 2020 16:07:53 +0200 Subject: [PATCH 055/167] analytics: avoid error log when there are no handlers --- react/features/analytics/functions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/react/features/analytics/functions.js b/react/features/analytics/functions.js index 65f9bfc18..ede59c555 100644 --- a/react/features/analytics/functions.js +++ b/react/features/analytics/functions.js @@ -117,7 +117,9 @@ export function createHandlers({ getState }: { getState: Function }) { }) .catch(e => { analytics.dispose(); - logger.error(e); + if (handlers.length !== 0) { + logger.error(e); + } return []; })); From b0188a71841c966122c3cce8c7023b7de8e32a82 Mon Sep 17 00:00:00 2001 From: plokta <7414587+plokta@users.noreply.github.com> Date: Sat, 11 Jul 2020 10:33:49 +0200 Subject: [PATCH 056/167] config: Add option to set preferred audio bitrate. (#7072) The maxaveragebitrate parameter to be used by Opus can be configured through the new opusMaxAvgBitrate config option. Values are restricted by Opus to integers between 6000 to 510000. Works for non-p2p only. move option to Audio section, add documentation Co-authored-by: plokta --- config.js | 5 +++++ react/features/base/config/configWhitelist.js | 1 + 2 files changed, 6 insertions(+) diff --git a/config.js b/config.js index c3b42b5b4..369b1ec4c 100644 --- a/config.js +++ b/config.js @@ -111,6 +111,11 @@ var config = { // participants and to enable it back a reload is needed. // startSilent: false + // Sets the preferred target bitrate for the Opus audio codec by setting its + // 'maxaveragebitrate' parameter. Currently not available in p2p mode. + // Valid values are in the range 6000 to 510000 + // opusMaxAvgBitrate: 20000, + // Video // Sets the preferred resolution (height) for local video. Defaults to 720. diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 3b32a2965..12d19631b 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -125,6 +125,7 @@ export default [ 'minParticipants', 'nick', 'openBridgeChannel', + 'opusMaxAvgBitrate', 'p2p', 'pcStatsInterval', 'preferH264', From e4ce3928dcaa18c52b351a30bf179ea8207b4a56 Mon Sep 17 00:00:00 2001 From: George Politis Date: Mon, 13 Jul 2020 18:20:59 +0300 Subject: [PATCH 057/167] feat: Exposes the max enabled resolution in the connection stats popover. (#7278) * feat: Exposes the max enabled resolution in the connection stats popover. * deps: lib-jitsi-meet@latest --- lang/main.json | 1 + package.json | 2 +- .../components/web/ConnectionIndicator.js | 2 ++ .../components/ConnectionStatsTable.js | 16 ++++++++++++++-- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lang/main.json b/lang/main.json index e20ef5778..7ba137976 100644 --- a/lang/main.json +++ b/lang/main.json @@ -110,6 +110,7 @@ "localaddress_plural": "Local addresses:", "localport": "Local port:", "localport_plural": "Local ports:", + "maxEnabledResolution": "send max", "more": "Show more", "packetloss": "Packet loss:", "quality": { diff --git a/package.json b/package.json index 68f51a385..51d98c46c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "jquery-i18next": "1.2.1", "js-md5": "0.6.1", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#cd008d726f1f57562eb5d8e6a3cd91c7e69826a0", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#304b0a2b4e18216d792f499c74fc24bc3849303e", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", diff --git a/react/features/connection-indicator/components/web/ConnectionIndicator.js b/react/features/connection-indicator/components/web/ConnectionIndicator.js index 5f92f52bb..f4b88fc70 100644 --- a/react/features/connection-indicator/components/web/ConnectionIndicator.js +++ b/react/features/connection-indicator/components/web/ConnectionIndicator.js @@ -342,6 +342,7 @@ class ConnectionIndicator extends AbstractConnectionIndicator { bridgeCount, e2eRtt, framerate, + maxEnabledResolution, packetLoss, region, resolution, @@ -358,6 +359,7 @@ class ConnectionIndicator extends AbstractConnectionIndicator { e2eRtt = { e2eRtt } framerate = { framerate } isLocalVideo = { this.props.isLocalVideo } + maxEnabledResolution = { maxEnabledResolution } onShowMore = { this._onToggleShowMore } packetLoss = { packetLoss } region = { region } diff --git a/react/features/connection-stats/components/ConnectionStatsTable.js b/react/features/connection-stats/components/ConnectionStatsTable.js index 581515b28..b5fc8a6a3 100644 --- a/react/features/connection-stats/components/ConnectionStatsTable.js +++ b/react/features/connection-stats/components/ConnectionStatsTable.js @@ -57,6 +57,12 @@ type Props = { */ isLocalVideo: boolean, + /** + * The send-side max enabled resolution (aka the highest layer that is not + * suspended on the send-side). + */ + maxEnabledResolution: number, + /** * Callback to invoke when the show additional stats link is clicked. */ @@ -380,8 +386,8 @@ class ConnectionStatsTable extends Component { * @returns {ReactElement} */ _renderResolution() { - const { resolution, t } = this.props; - const resolutionString = Object.keys(resolution || {}) + const { resolution, maxEnabledResolution, t } = this.props; + let resolutionString = Object.keys(resolution || {}) .map(ssrc => { const { width, height } = resolution[ssrc]; @@ -389,6 +395,12 @@ class ConnectionStatsTable extends Component { }) .join(', ') || 'N/A'; + if (maxEnabledResolution && maxEnabledResolution < 720) { + const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution'); + + resolutionString += ` (${maxEnabledResolutionTitle} ${maxEnabledResolution}p)`; + } + return ( From 5b897094833207bc6a8bc1afbbdc46e255d11ee3 Mon Sep 17 00:00:00 2001 From: abora8x8 <56257143+abora8x8@users.noreply.github.com> Date: Mon, 13 Jul 2020 19:29:35 +0300 Subject: [PATCH 058/167] Add hook for creating lobby before participants join (#7273) * Add hook for create lobby * Remove duplicated code --- .../prosody-plugins/mod_muc_lobby_rooms.lua | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/resources/prosody-plugins/mod_muc_lobby_rooms.lua b/resources/prosody-plugins/mod_muc_lobby_rooms.lua index f3a912fbe..63cd55835 100644 --- a/resources/prosody-plugins/mod_muc_lobby_rooms.lua +++ b/resources/prosody-plugins/mod_muc_lobby_rooms.lua @@ -153,6 +153,20 @@ function filter_session(session) end end +function attach_lobby_room(room) + local node = jid_split(room.jid); + local lobby_room_jid = node .. '@' .. lobby_muc_component_config; + if not lobby_muc_service.get_room_from_jid(lobby_room_jid) then + local new_room = lobby_muc_service.create_room(lobby_room_jid); + module:log("debug","Lobby room jid = %s created",lobby_room_jid); + new_room.main_room = room; + room._data.lobbyroom = new_room; + room:save(true); + return true + end + return false +end + -- process a host module directly if loaded or hooks to wait for its load function process_host_module(name, callback) local function process_host(host) @@ -251,15 +265,14 @@ process_host_module(main_muc_component_config, function(host_module, host) -- hooks when lobby is enabled to create its room, only done here or by admin host_module:hook('muc-config-submitted', function(event) local actor, room = event.actor, event.room; + local actor_node = jid_split(actor); + if actor_node == 'focus' then + return; + end local members_only = event.fields['muc#roomconfig_membersonly'] and true or nil; if members_only then - local node = jid_split(room.jid); - - local lobby_room_jid = node .. '@' .. lobby_muc_component_config; - if not lobby_muc_service.get_room_from_jid(lobby_room_jid) then - local new_room = lobby_muc_service.create_room(lobby_room_jid); - new_room.main_room = room; - room._data.lobbyroom = new_room; + local lobby_created = attach_lobby_room(room); + if lobby_created then event.status_codes['104'] = true; notify_lobby_enabled(room, actor, true); end @@ -373,6 +386,14 @@ function update_session(event) end end +function handle_create_lobby(event) + local room = event.room; + room:set_members_only(true); + module:log("info","Set room jid = %s as members only",room.jid); + attach_lobby_room(room) +end + module:hook_global('bosh-session', update_session); module:hook_global('websocket-session', update_session); module:hook_global('config-reloaded', load_config); +module:hook_global('create-lobby-room', handle_create_lobby); \ No newline at end of file From a697caea031bb1b2c68d4094c19b03da52d27003 Mon Sep 17 00:00:00 2001 From: Julian Vos Date: Tue, 14 Jul 2020 13:42:03 +0200 Subject: [PATCH 059/167] lang: update Dutch translation --- lang/main-nl.json | 700 +++++++++++++++++++++++++++++----------------- 1 file changed, 438 insertions(+), 262 deletions(-) diff --git a/lang/main-nl.json b/lang/main-nl.json index add234baf..3d3d7f538 100644 --- a/lang/main-nl.json +++ b/lang/main-nl.json @@ -1,90 +1,116 @@ { "addPeople": { "add": "Uitnodigen", + "addContacts": "Nodig uw contacten uit", + "copyInvite": "Uitnodiging voor vergadering kopiëren", + "copyLink": "Link naar vergadering kopiëren", + "copyStream": "Link naar livestream kopiëren", "countryNotSupported": "Deze bestemming wordt nog niet ondersteund.", - "countryReminder": "Belt u naar een bestemming buiten de Verenigde Staten? Vergeet dan niet de landcode te gebruiken.", + "countryReminder": "Belt u buiten de Verenigde Staten? Vergeet dan niet met de landcode te beginnen.", + "defaultEmail": "Uw Standaard E-mail", "disabled": "U kunt geen personen uitnodigen.", - "failedToAdd": "Het toevoegen van leden is mislukt", - "footerText": "Uitgaande oproep is uitgeschakeld.", - "loading": "Personen en telefoonnummers zoeken", - "loadingNumber": "Telefoonnummer valideren", - "loadingPeople": "Personen zoeken om uit te nodigen", + "failedToAdd": "Het toevoegen van deelnemers is mislukt", + "footerText": "Uitgaande oproepen zijn uitgeschakeld.", + "googleEmail": "Google E-mail", + "inviteMoreHeader": "U bent de enige in de vergadering", + "inviteMoreMailSubject": "Deelnemen aan {{appName}}-vergadering", + "inviteMorePrompt": "Nodig meer personen uit", + "linkCopied": "Link gekopieerd naar klembord", + "loading": "Personen en telefoonnummers aan het zoeken", + "loadingNumber": "Telefoonnummer aan het valideren", + "loadingPeople": "Personen om uit te nodigen aan het zoeken", "noResults": "Geen resultaten die overeenkomen met de zoekopdracht", "noValidNumbers": "Voer een telefoonnummer in", + "outlookEmail": "Outlook E-mail", "searchNumbers": "Telefoonnummers toevoegen", "searchPeople": "Personen zoeken", "searchPeopleAndNumbers": "Personen zoeken of hun telefoonnummers toevoegen", - "telephone": "Telefoonnummer: {{number}}", - "title": "Personen uitnodigen voor deze vergadering" + "shareInvite": "Uitnodiging voor vergadering delen", + "shareLink": "Deel de link naar de vergadering om anderen uit te nodigen", + "shareStream": "Deel de link naar de livestream", + "telephone": "Telefoon: {{number}}", + "title": "Personen uitnodigen voor deze vergadering", + "yahooEmail": "Yahoo E-mail" }, "audioDevices": { "bluetooth": "Bluetooth", "headphones": "Hoofdtelefoon", "phone": "Telefoon", - "speaker": "Speaker" + "speaker": "Speaker", + "none": "Geen audioapparaten beschikbaar" }, "audioOnly": { - "audioOnly": "Alleen audio" + "audioOnly": "Lage bandbreedte" }, "calendarSync": { - "addMeetingURL": "Een link naar de vergadering toevoegen", - "confirmAddLink": "Wilt u een Jitsi-link aan deze gebeurtenis toevoegen?", + "addMeetingURL": "Een link naar een vergadering toevoegen", + "confirmAddLink": "Wilt u een Jitsi-link aan deze afspraak toevoegen?", "error": { "appConfiguration": "De agenda-integratie is niet juist ingesteld.", - "generic": "Er is een fout opgetreden. Controleer de agenda-instellingen of vernieuw de agenda.", - "notSignedIn": "Er is een fout opgetreden tijdens de verificatie voor het weergeven van agendagebeurtenissen. Controleer de agenda-instellingen en probeer u opnieuw aan te melden." + "generic": "Er is een fout opgetreden. Controleer uw agenda-instellingen of vernieuw de agenda.", + "notSignedIn": "Er is een fout opgetreden tijdens de authenticering voor het zien van agenda-afspraken. Controleer uw agenda-instellingen en probeer u opnieuw aan te melden." }, "join": "Deelnemen", "joinTooltip": "Deelnemen aan de vergadering", "nextMeeting": "volgende vergadering", - "noEvents": "Er zijn geen gebeurtenissen gepland.", + "noEvents": "Er zijn geen aankomende afspraken gepland.", "ongoingMeeting": "actieve vergadering", "permissionButton": "Instellingen openen", - "permissionMessage": "U hebt een machtiging voor Agenda nodig om uw afspraken weer te geven in de app.", + "permissionMessage": "Het Agenda-toegangsrecht is nodig om uw afspraken in de app te zien.", "refresh": "Agenda vernieuwen", "today": "Vandaag" }, "chat": { "error": "Fout: uw bericht \"{{originalText}}\" is niet verzonden. Reden: {{error}}", - "fieldPlaceHolder": "Type hier je bericht", + "fieldPlaceHolder": "Typ hier uw bericht", "messagebox": "Typ een bericht", "messageTo": "Privébericht aan {{recipient}}", - "noMessagesMessage": "Er zijn nog geen berichten in deze bijkeenkomst. Begin een gesprek!", + "noMessagesMessage": "Er zijn nog geen berichten in de vergadering. Begin hier een gesprek!", "nickname": { "popover": "Kies een bijnaam", - "title": "Voer een bijnaam in om de chatfunctie te gebruiken" + "title": "Voer een bijnaam in om chat te gebruiken" }, "privateNotice": "Privébericht aan {{recipient}}", "title": "Chat", - "you": "jij" + "you": "u" + }, + "chromeExtensionBanner": { + "installExtensionText": "Installeer de extensie voor Google Calendar en Office 365 integratie", + "buttonText": "Installeer Chrome Extensie", + "dontShowAgain": "Laat me dit niet meer zien" }, "connectingOverlay": { - "joiningRoom": "Er wordt verbinding gemaakt met de vergadering…" + "joiningRoom": "U wordt verbonden met uw vergadering..." }, "connection": { - "ATTACHED": "Bijgesloten", - "AUTHENTICATING": "Verifiëren", - "AUTHFAIL": "Verificatie mislukt", + "ATTACHED": "Bijgevoegd", + "AUTHENTICATING": "Authenticeren", + "AUTHFAIL": "Authenticering mislukt", "CONNECTED": "Verbonden", "CONNECTING": "Verbinding maken", "CONNFAIL": "Verbinding mislukt", "DISCONNECTED": "Verbinding verbroken", "DISCONNECTING": "Verbinding verbreken", "ERROR": "Fout", - "RECONNECTING": "Er is een netwerkprobleem opgetreden. Er wordt opnieuw verbinding gemaakt..." + "FETCH_SESSION_ID": "Sessie-id ophalen...", + "GET_SESSION_ID_ERROR": "Fout bij ophalen sessie-id: {{code}}", + "GOT_SESSION_ID": "Sessie-id ophalen... Klaar", + "LOW_BANDWIDTH": "Video voor {{displayName}} is uitgeschakeld om bandbreedte te besparen" }, "connectionindicator": { "address": "Adres:", "bandwidth": "Geschatte bandbreedte:", "bitrate": "Bitrate:", - "bridgeCount": "Aantal servers:", + "bridgeCount": "Aantal servers: ", "connectedTo": "Verbonden met:", + "e2e_rtt": "E2E RTT:", "framerate": "Framesnelheid:", "less": "Minder weergeven", "localaddress": "Lokaal adres:", "localaddress_plural": "Lokale adressen:", "localport": "Lokale poort:", "localport_plural": "Lokale poorten:", + "maxEnabledResolution": "verstuur max", "more": "Meer weergeven", "packetloss": "Pakketverlies:", "quality": { @@ -101,8 +127,7 @@ "resolution": "Resolutie:", "status": "Verbinding:", "transport": "Transport:", - "transport_plural": "Transporten:", - "turn": " (draaien)" + "transport_plural": "Transporten:" }, "dateUtils": { "earlier": "Eerder", @@ -110,23 +135,24 @@ "yesterday": "Gisteren" }, "deepLinking": { - "appNotInstalled": "U hebt de mobiele app {{app}} nodig om op uw telefoon aan deze vergadering deel te nemen.", + "appNotInstalled": "U hebt de mobiele app {{app}} nodig om aan deze vergadering deel te nemen via uw telefoon.", "description": "Gebeurt er niets? Er is geprobeerd uw vergadering te starten in de desktop-app {{app}}. Probeer het opnieuw of start de vergadering in de web-app {{app}}.", - "descriptionWithoutWeb": "", + "descriptionWithoutWeb": "Gebeurt er niets? Er is geprobeerd om uw vergadering te starten in de desktop-app {{app}}", "downloadApp": "Download de app", - "ifDoNotHaveApp": "Als u de app nog niet heeft:", - "ifHaveApp": "Als u de app al heeft:", - "joinInApp": "Neem deel aan de vergadering met de app", - "launchWebButton": "Starten via web", - "title": "Uw vergadering wordt gestart in {{app}}…", - "tryAgainButton": "Opnieuw proberen op desktop" + "ifDoNotHaveApp": "Als u de app nog niet hebt:", + "ifHaveApp": "Als u de app al hebt:", + "joinInApp": "Deelnemen aan deze vergadering met de app", + "launchWebButton": "Starten in web", + "title": "Uw vergadering wordt gestart in {{app}}...", + "tryAgainButton": "Opnieuw proberen in desktop" }, "defaultLink": "bijv. {{url}}", + "defaultNickname": "bijv. Jannie Roze", "deviceError": { - "cameraError": "Geen toegang tot de camera", - "cameraPermission": "Fout bij het verkrijgen van toestemming voor de camera", - "microphoneError": "Geen toegang tot de microfoon", - "microphonePermission": "Fout bij het verkrijgen van toestemming voor de microfoon" + "cameraError": "Toegang tot uw camera is mislukt", + "cameraPermission": "Fout bij verkrijgen toegangsrecht camera", + "microphoneError": "Toegang tot uw microfoon is mislukt", + "microphonePermission": "Fout bij verkrijgen toegangsrecht microfoon" }, "deviceSelection": { "noPermission": "Geen toestemming verleend", @@ -138,114 +164,128 @@ "accessibilityLabel": { "liveStreaming": "Livestream" }, + "add": "Toevoegen", "allow": "Toestaan", - "alreadySharedVideoMsg": "Er wordt al een video gedeeld door een andere deelnemer. In deze vergadering kan slechts één video tegelijkertijd worden gedeeld.", - "alreadySharedVideoTitle": "Slechts één gedeelde video tegelijkertijd toegestaan", + "alreadySharedVideoMsg": "Er wordt al een video gedeeld door een andere deelnemer. Deze vergadering staat slechts één gedeelde video tegelijkertijd toe.", + "alreadySharedVideoTitle": "Slechts één gedeelde video tegelijkertijd is toegestaan", "applicationWindow": "Toepassingsvenster", "Back": "Terug", "cameraConstraintFailedError": "Uw camera voldoet niet aan alle vereiste beperkingen.", "cameraNotFoundError": "Camera niet gevonden.", - "cameraNotSendingData": "Er is geen toegang tot uw camera verkregen. Controleer of dit apparaat wordt gebruikt door een andere toepassing, selecteer een ander apparaat in de instellingen of laad de toepassing opnieuw.", - "cameraNotSendingDataTitle": "Geen toegang tot de camera", - "cameraPermissionDeniedError": "U hebt geen toestemming verleend voor het gebruik van de camera. U kunt wel deelnemen aan de vergadering, maar anderen kunnen u niet zien. Gebruik de cameraknop in de adresbalk als u dit wilt wijzigen.", + "cameraNotSendingData": "Er is geen toegang tot uw camera verkregen. Controleer of dit apparaat wordt gebruikt door een andere toepassing, selecteer een ander apparaat vanuit de instellingen of probeer de toepassing te herladen.", + "cameraNotSendingDataTitle": "Geen toegang tot camera", + "cameraPermissionDeniedError": "U hebt geen toestemming verleend om uw camera te gebruiken. U kunt wel deelnemen aan de vergadering, maar anderen kunnen u niet zien. Gebruik de cameraknop in de adresbalk om dit op te lossen.", "cameraUnknownError": "Kan de camera om een onbekende reden niet gebruiken.", - "cameraUnsupportedResolutionError": "De camera biedt geen ondersteuning voor de vereiste videoresolutie.", + "cameraUnsupportedResolutionError": "Uw camera ondersteunt de vereiste videoresolutie niet.", "Cancel": "Annuleren", "close": "Sluiten", - "conferenceDisconnectMsg": "Controleer de netwerkverbinding. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken…", - "conferenceDisconnectTitle": "De verbinding is verbroken.", - "conferenceReloadMsg": "We proberen het probleem op te lossen. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken…", - "conferenceReloadTitle": "Er is iets misgegaan.", + "conferenceDisconnectMsg": "Controleer uw netwerkverbinding. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken...", + "conferenceDisconnectTitle": "Uw verbinding is verbroken.", + "conferenceReloadMsg": "Er wordt geprobeerd om dit op te lossen. Over {{seconds}} sec. wordt opnieuw geprobeerd verbinding te maken...", + "conferenceReloadTitle": "Er is helaas iets misgegaan.", "confirm": "Bevestigen", "confirmNo": "Nee", "confirmYes": "Ja", - "connectError": "Er is iets misgegaan. Er kan geen verbinding met de vergadering worden gemaakt.", - "connectErrorWithMsg": "Er is iets misgegaan. Er kan geen verbinding met de vergadering worden gemaakt: {{msg}}", + "connectError": "Oeps! Er is iets misgegaan en er kon geen verbinding met de vergadering worden gemaakt.", + "connectErrorWithMsg": "Oeps! Er is iets misgegaan en er kon geen verbinding met de vergadering worden gemaakt: {{msg}}", "connecting": "Verbinding maken", "contactSupport": "Contact opnemen met ondersteuning", "copy": "Kopiëren", "dismiss": "Negeren", - "displayNameRequired": "Weergavenaam is vereist", + "displayNameRequired": "Hallo! Wat is uw naam?", "done": "Gereed", - "enterDisplayName": "Voer uw weergavenaam in", + "e2eeDescription": "Eind-tot-Eind-Versleuteling is momenteel EXPERIMENTEEL. Houd er rekening mee dat inschakelen van eind-tot-eind-versleuteling de door de server geleverde services zal uitschakelen zoals: opnemen, livestreamen en deelname via telefoon. Houd er ook rekening mee dat de vergadering alleen zal werken voor personen die deelnemen vanaf browsers met ondersteuning voor insertable streams.", + "e2eeLabel": "Sleutel", + "e2eeNoKey": "Geen", + "e2eeToggleSet": "Sleutel instellen", + "e2eeSet": "Instellen", + "e2eeWarning": "WAARSCHUWING: Niet alle deelnemers in deze vergadering lijken ondersteuning te hebben voor eind-tot-eind-versleuteling. Als u het inschakelt zullen zij u niet kunnen zien of horen.", + "enterDisplayName": "Voer hier uw naam in", "error": "Fout", - "externalInstallationMsg": "U moet onze extensie voor het delen van het bureaublad installeren.", - "externalInstallationTitle": "Extensie vereist", - "goToStore": "Naar de webwinkel", - "gracefulShutdown": "Onze service is momenteel niet beschikbaar wegens onderhoud. Probeer het later opnieuw.", + "gracefulShutdown": "Onze service is momenteel niet beschikbaar vanwege onderhoud. Probeer het later opnieuw.", "IamHost": "Ik ben de host", - "incorrectRoomLockPassword": "", - "incorrectPassword": "Gebruikersnaam of wachtwoord onjuist", - "inlineInstallationMsg": "U moet onze extensie voor het delen van het bureaublad installeren.", - "inlineInstallExtension": "Nu installeren", - "internalError": "Er is iets misgegaan. De volgende fout is opgetreden: {{error}}", + "incorrectRoomLockPassword": "Onjuist wachtwoord", + "incorrectPassword": "Onjuiste gebruikersnaam of wachtwoord", + "internalError": "Oeps! Er is iets misgegaan. De volgende fout trad op: {{error}}", "internalErrorTitle": "Interne fout", - "kickMessage": "U bent uit de vergadering verwijderd.", + "kickMessage": "U kunt contact opnemen met {{participantDisplayName}} voor meer informatie.", "kickParticipantButton": "Verwijderen", "kickParticipantDialog": "Weet u zeker dat u deze deelnemer wilt verwijderen?", "kickParticipantTitle": "Deze deelnemer verwijderen?", - "kickTitle": "Verwijderd uit vergadering", - "liveStreaming": "Livestream", - "liveStreamingDisabledForGuestTooltip": "Gasten kunnen de livestream niet starten.", - "liveStreamingDisabledTooltip": "Het starten van de livestream is uitgeschakeld.", + "kickTitle": "Oei! {{ParticipantDisplayName}} heeft u uit de vergadering verwijderd", + "liveStreaming": "Livestreamen", + "liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Niet mogelijk tijdens opnemen", + "liveStreamingDisabledForGuestTooltip": "Gasten kunnen geen livestream starten.", + "liveStreamingDisabledTooltip": "Livestream starten uitgeschakeld.", "lockMessage": "Het vergrendelen van de vergadering is mislukt.", - "lockRoom": "Wachtwoord voor vergadering toevoegen", + "lockRoom": "$t(lockRoomPasswordUppercase) voor vergadering toevoegen", "lockTitle": "Vergrendelen mislukt", "logoutQuestion": "Weet u zeker dat u zich wilt afmelden en de vergadering wilt stoppen?", "logoutTitle": "Afmelden", - "maxUsersLimitReached": "Het maximale aantal leden is bereikt. De vergadering is vol. Neem contact op met de eigenaar van de vergadering of probeer het later opnieuw.", - "maxUsersLimitReachedTitle": "Maximaal aantal leden bereikt", - "micConstraintFailedError": "Uw microfoon voldoet niet aan alle vereiste randvoorwaarden.", + "maxUsersLimitReached": "Het maximale aantal deelnemers is bereikt. De vergadering is vol. Neem contact op met de eigenaar van de vergadering of probeer het later opnieuw!", + "maxUsersLimitReachedTitle": "Maximaal aantal deelnemers bereikt", + "micConstraintFailedError": "Uw microfoon voldoet niet aan alle vereiste beperkingen.", "micNotFoundError": "Microfoon niet gevonden.", - "micNotSendingData": "Er is geen toegang tot uw microfoon verkregen. Selecteer een ander apparaat in de instellingen of laad de toepassing opnieuw.", - "micNotSendingDataTitle": "Geen toegang tot de microfoon", - "micPermissionDeniedError": "U hebt geen toestemming verleend voor het gebruik van de microfoon. U kunt wel deelnemen aan de vergadering, maar anderen kunnen u niet horen. Gebruik de cameraknop in de adresbalk als u dit wilt wijzigen.", + "micNotSendingData": "Ga naar de instellingen van uw computer om het dempen van uw microfoon op te heffen en het geluidsniveau aan te passen", + "micNotSendingDataTitle": "Uw microfoon is gedempt door uw systeeminstellingen", + "micPermissionDeniedError": "U hebt geen toestemming verleend om uw microfoon te gebruiken. U kunt wel deelnemen aan de vergadering, maar anderen kunnen u niet horen. Gebruik de cameraknop in de adresbalk om dit op te lossen.", "micUnknownError": "Kan de microfoon om een onbekende reden niet gebruiken.", - "muteParticipantBody": "U kunt het dempen van anderen niet opheffen, maar zij kunnen dit wel op elk gewenst moment voor zichzelf doen.", + "muteEveryoneElseDialog": "Eenmaal gedempt kunt u het dempen niet opheffen, maar zij kunnen dit wel ieder moment zelf doen.", + "muteEveryoneElseTitle": "Iedereen dempen behalve {{whom}}?", + "muteEveryoneDialog": "Weet u zeker dat u iedereen wilt dempen? U kunt het dempen niet opheffen, maar zij kunnen dit wel ieder moment zelf doen.", + "muteEveryoneTitle": "Iedereen dempen?", + "muteEveryoneSelf": "uzelf", + "muteEveryoneStartMuted": "Iedereen start vanaf nu gedempt", + "muteParticipantBody": "U kunt het dempen niet opheffen, maar zij kunnen dit wel ieder moment zelf doen.", "muteParticipantButton": "Dempen", - "muteParticipantDialog": "Weet u zeker dat u deze deelnemer wilt dempen? U kunt het dempen niet opheffen, maar deze deelnemer kan dat wel op elk gewenst moment zelf doen.", + "muteParticipantDialog": "Weet u zeker dat u deze deelnemer wilt dempen? U kunt het dempen niet opheffen, maar deze deelnemer kan dit wel ieder moment zelf doen.", "muteParticipantTitle": "Deze deelnemer dempen?", "Ok": "OK", - "passwordLabel": "Wachtwoord", - "passwordNotSupported": "Het instellen van een wachtwoord voor een vergadering wordt niet ondersteund.", - "passwordNotSupportedTitle": "Wachtwoord niet ondersteund", - "passwordRequired": "Wachtwoord vereist", - "popupError": "Er wordt een pop-upvenster van deze site geblokkeerd door uw browser. Ga naar de beveiligingsinstellingen van uw browser, schakel pop-upvensters in en probeer het opnieuw.", - "popupErrorTitle": "Pop-up wordt geblokkeerd", + "passwordLabel": "De vergadering is vergrendeld door een deelnemer. Voer het $t(lockRoomPassword) in om deel te nemen.", + "passwordNotSupported": "Instellen van een $t(lockRoomPassword) voor de vergadering wordt niet ondersteund.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) niet ondersteund", + "passwordRequired": "$t(lockRoomPasswordUppercase) vereist", + "popupError": "Uw browser blokkeert pop-upvensters van deze site. Schakel pop-ups in vanuit de beveiligingsinstellingen van uw browser en probeer het opnieuw.", + "popupErrorTitle": "Pop-up geblokkeerd", + "readMore": "meer", "recording": "Opnemen", + "recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Niet mogelijk tijdens een livestream", "recordingDisabledForGuestTooltip": "Gasten kunnen geen opnamen starten.", - "recordingDisabledTooltip": "Het starten van opnamen is uitgeschakeld.", + "recordingDisabledTooltip": "Opname starten uitgeschakeld.", "rejoinNow": "Nu opnieuw deelnemen", - "remoteControlAllowedMessage": "{{user}} heeft uw aanvraag voor extern beheer geaccepteerd.", - "remoteControlDeniedMessage": "{{user}} heeft uw aanvraag voor extern beheer geweigerd.", - "remoteControlErrorMessage": "Er is een fout opgetreden tijdens het aanvragen van machtigingen voor extern beheer bij {{user}}.", - "remoteControlRequestMessage": "Wilt u {{user}} een machtiging verlenen om uw bureaublad extern te beheren?", - "remoteControlShareScreenWarning": "Let op: als u 'Toestaan' kiest, wordt uw scherm gedeeld.", - "remoteControlStopMessage": "De sessie voor extern beheer is beëindigd.", + "remoteControlAllowedMessage": "{{user}} heeft uw verzoek om extern beheer geaccepteerd.", + "remoteControlDeniedMessage": "{{user}} heeft uw verzoek om extern beheer geweigerd.", + "remoteControlErrorMessage": "Er is een fout opgetreden tijdens het verzoek om toestemming voor extern beheer van {{user}}!", + "remoteControlRequestMessage": "Wilt u {{user}} toestemming verlenen om uw bureaublad extern te beheren?", + "remoteControlShareScreenWarning": "Let op: als u op 'Toestaan' drukt, wordt uw scherm gedeeld!", + "remoteControlStopMessage": "De sessie van extern beheer is geëindigd!", "remoteControlTitle": "Extern beheer van bureaublad", "Remove": "Verwijderen", - "removePassword": "Wachtwoord verwijderen", + "removePassword": "$t(lockRoomPasswordUppercase) verwijderen", "removeSharedVideoMsg": "Weet u zeker dat u uw gedeelde video wilt verwijderen?", "removeSharedVideoTitle": "Gedeelde video verwijderen", "reservationError": "Fout in reserveringssysteem", "reservationErrorMsg": "Foutcode: {{code}}, bericht: {{msg}}", "retry": "Opnieuw proberen", - "screenSharingFailedToInstall": "De installatie van de extensie voor het delen van het scherm is mislukt.", - "screenSharingFailedToInstallTitle": "Installatie van extensie voor het delen van het scherm is mislukt", - "screenSharingFirefoxPermissionDeniedError": "Er is iets misgegaan tijdens het delen van uw scherm. Controleer of u hier toestemming voor hebt verleend.", - "screenSharingFirefoxPermissionDeniedTitle": "Scherm delen kan niet worden gestart.", - "screenSharingPermissionDeniedError": "Er is iets misgegaan met de permissies voor het delen van het scherm. Laad de toepassing opnieuw en probeer het nog eens.", + "screenSharingAudio": "Deel audio", + "screenSharingFailed": "Oeps! Er is iets misgegaan, de schermdeling kon niet worden gestart!", + "screenSharingFailedTitle": "Schermdeling mislukt!", + "screenSharingPermissionDeniedError": "Oeps! Er is iets misgegaan met uw toegangsrechten voor schermdeling. Herlaad en probeer opnieuw.", + "sendPrivateMessage": "U hebt recentelijk een privébericht ontvangen. Bent u van plan daar privé op te reageren, of wilt u uw bericht naar de groep sturen?", + "sendPrivateMessageCancel": "Stuur naar de groep", + "sendPrivateMessageOk": "Privé versturen", + "sendPrivateMessageTitle": "Privé versturen?", "serviceUnavailable": "Service niet beschikbaar", "sessTerminated": "Gesprek beëindigd", "Share": "Delen", - "shareVideoLinkError": "Geef een juiste YouTube-link op.", + "shareVideoLinkError": "Geef een juiste YouTube-link op", "shareVideoTitle": "Een video delen", "shareYourScreen": "Uw scherm delen", - "shareYourScreenDisabled": "Scherm delen is uitgeschakeld.", + "shareYourScreenDisabled": "Schermdeling is uitgeschakeld.", "shareYourScreenDisabledForGuest": "Gasten kunnen hun scherm niet delen.", "startLiveStreaming": "Livestream starten", "startRecording": "Opname starten", - "startRemoteControlErrorMessage": "Er is een fout opgetreden tijdens het starten van de sessie voor extern beheer.", + "startRemoteControlErrorMessage": "Er is een fout opgetreden tijdens het starten van de sessie van extern beheer.", "stopLiveStreaming": "Livestream stoppen", "stopRecording": "Opname stoppen", "stopRecordingWarning": "Weet u zeker dat u de opname wilt stoppen?", @@ -254,28 +294,34 @@ "Submit": "Verzenden", "thankYou": "Bedankt voor het gebruik van {{appName}}.", "token": "token", - "tokenAuthFailed": "U hebt geen toestemming om aan dit gesprek deel te nemen.", - "tokenAuthFailedTitle": "Verificatie mislukt", + "tokenAuthFailed": "Sorry, u bent niet toegestaan om deel te nemen aan dit gesprek.", + "tokenAuthFailedTitle": "Authenticering mislukt", "transcribing": "Transcriberen", - "unlockRoom": "Wachtwoord voor vergadering verwijderen", + "unlockRoom": "$t(lockRoomPasswordUppercase) voor vergadering verwijderen", "userPassword": "gebruikerswachtwoord", - "WaitForHostMsg": "De vergadering {{room}} is nog niet gestart. Verifieer de vergadering als u de host bent. Anders wacht u tot de host aanwezig is.", - "WaitForHostMsgWOk": "De vergadering {{room}} is nog niet gestart. Als u de host bent, drukt u op OK om te verifiëren. Anders wacht u tot de host aanwezig is.", - "WaitingForHost": "Wachten op de host…", + "WaitForHostMsg": "De vergadering {{room}} is nog niet gestart. Authenticeer uzelf als u de host bent. Anders wacht u tot de host aanwezig is.", + "WaitForHostMsgWOk": "De vergadering {{room}} is nog niet gestart. Als u de host bent, drukt u op 'OK' om uzelf te authenticeren. Anders wacht u tot de host aanwezig is.", + "WaitingForHost": "Wachten op de host...", "Yes": "Ja", "yourEntireScreen": "Uw gehele scherm" }, "dialOut": { "statusMessage": "is nu {{status}}" }, + "documentSharing": { + "title": "Gedeeld Document" + }, + "e2ee": { + "labelToolTip": "Audio- en Videocommunicatie in dit gesprek is eind-tot-eind-versleuteld" + }, "feedback": { "average": "Gemiddeld", "bad": "Slecht", "detailsLabel": "We horen er graag meer over.", "good": "Goed", - "rateExperience": "Beoordeel uw ervaring tijdens de vergadering", - "veryBad": "Zeer slecht", - "veryGood": "Zeer goed" + "rateExperience": "Beoordeel uw vergaderervaring", + "veryBad": "Zeer Slecht", + "veryGood": "Zeer Goed" }, "incomingCall": { "answer": "Beantwoorden", @@ -286,30 +332,30 @@ }, "info": { "accessibilityLabel": "Informatie weergeven", - "addPassword": "Wachtwoord toevoegen", - "cancelPassword": "Wachtwoord annuleren", + "addPassword": "$t(lockRoomPasswordUppercase) toevoegen", + "cancelPassword": "$t(lockRoomPasswordUppercase) annuleren", "conferenceURL": "Link:", "country": "Land", - "dialANumber": "Als u wilt deelnemen aan de vergadering, belt u een van deze nummers en voert u vervolgens de pincode in.", + "dialANumber": "Om deel te nemen aan uw vergadering, belt u een van deze nummers en voert u vervolgens de pincode in.", "dialInConferenceID": "Pincode:", - "dialInNotSupported": "Inbellen wordt momenteel niet ondersteund.", + "dialInNotSupported": "Sorry, inbellen wordt momenteel niet ondersteund.", "dialInNumber": "Inbellen:", "dialInSummaryError": "Fout bij het ophalen van inbelgegevens. Probeer het later opnieuw.", "dialInTollFree": "Gratis", - "genericError": "Er is iets misgegaan.", - "inviteLiveStream": "Klik op de volgende link als u de livestream van deze vergadering wilt bekijken: {{url}}", - "invitePhone": "Met één druk op de knop inbellen: {{number}},,{{conferenceID}}#\n", - "invitePhoneAlternatives": "", + "genericError": "Oeps, er is iets misgegaan.", + "inviteLiveStream": "Om de livestream van deze vergadering te bekijken, klikt u op deze link: {{url}}", + "invitePhone": "Om deel te nemen via uw telefoon, tik hierop: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Op zoek naar een ander inbelnummer?\nBekijk de inbelnummers voor de vergadering: {{url}}\n\n\nAls u ook inbelt via een conferentietelefoon, neem dan deel zonder audio: {{silentUrl}}", "inviteURLFirstPartGeneral": "U bent uitgenodigd om aan een vergadering deel te nemen.", "inviteURLFirstPartPersonal": "{{name}} nodigt u uit voor een vergadering.\n", - "inviteURLSecondPart": "\nDeelnemen aan vergadering:\n{{url}}\n", + "inviteURLSecondPart": "\nDeelnemen aan de vergadering:\n{{url}}\n", "liveStreamURL": "Livestream:", "moreNumbers": "Meer nummers", "noNumbers": "Geen inbelnummers.", "noPassword": "Geen", "noRoom": "Er is geen ruimte opgegeven om naar in te bellen.", "numbers": "Inbelnummers", - "password": "Wachtwoord:", + "password": "$t(lockRoomPasswordUppercase):", "title": "Delen", "tooltip": "De link en inbelgegevens voor deze vergadering delen", "label": "Vergaderingsgegevens" @@ -317,8 +363,8 @@ "inviteDialog": { "alertText": "Niet alle deelnemers zijn uitgenodigd.", "header": "Uitnodigen", - "searchCallOnlyPlaceholder": "Telefoonnummer invoeren", - "searchPeopleOnlyPlaceholder": "Deelnemers zoeken", + "searchCallOnlyPlaceholder": "Voer telefoonnummer in", + "searchPeopleOnlyPlaceholder": "Zoek deelnemers", "searchPlaceholder": "Deelnemer of telefoonnummer", "send": "Verzenden" }, @@ -326,7 +372,7 @@ "msg": "Er is een fout opgetreden.", "retry": "Opnieuw proberen", "support": "Ondersteuning", - "supportMsg": "Als dit probleem opnieuw optreedt, neemt u contact op met" + "supportMsg": "Als dit blijft gebeuren, neemt u contact op met" }, "keyboardShortcuts": { "focusLocal": "Focus op uw video", @@ -338,39 +384,46 @@ "pushToTalk": "Druk om te spreken", "raiseHand": "Uw hand opsteken of laten zakken", "showSpeakerStats": "Sprekerstatistieken weergeven", - "toggleChat": "Chatgesprek openen of sluiten", - "toggleFilmstrip": "Toon of verberg videominiaturen", - "toggleScreensharing": "Schakelen tussen camera en het delen van het scherm", + "toggleChat": "Chat openen of sluiten", + "toggleFilmstrip": "Videominiaturen weergeven of verbergen", + "toggleScreensharing": "Wisselen tussen camera en schermdeling", "toggleShortcuts": "Sneltoetsen weergeven of verbergen", - "videoMute": "Uw camera starten of stoppen" + "videoMute": "Uw camera starten of stoppen", + "videoQuality": "Kwaliteit van gesprek beheren" }, "liveStreaming": { - "busy": "We werken aan het vrijmaken van streaming-middelen. Probeer het over enkele minuten opnieuw.", + "limitNotificationDescriptionWeb": "Vanwege een grote vraag zal uw stream beperkt worden tot {{limit}} min. Voor ongelimiteerd streamen, probeer {{app}}.", + "limitNotificationDescriptionNative": "Uw stream zal beperkt worden tot {{limit}} min. Voor ongelimiteerd streamen, probeer {{app}}.", + "busy": "Er wordt gewerkt aan het vrijmaken van streamingmiddelen. Probeer het over enkele minuten opnieuw.", "busyTitle": "Alle streamers zijn momenteel bezet", "changeSignIn": "Wissel van account.", - "choose": "Een livestream kiezen", + "choose": "Kies een livestream", "chooseCTA": "Kies een streamingoptie. U bent momenteel aangemeld als {{email}}.", - "enterStreamKey": "Voer hier de sleutel van YouTube voor de livestream in.", - "error": "Livestream is mislukt. Probeer het opnieuw.", + "enterStreamKey": "Voer hier uw livestream-sleutel van YouTube in.", + "error": "Livestreamen is mislukt. Probeer het opnieuw.", "errorAPI": "Er is een fout opgetreden tijdens het openen van uw YouTube-uitzendingen. Meld u opnieuw aan.", - "errorLiveStreamNotEnabled": "Livestreaming is niet ingeschakeld voor {{email}}. Schakel livestreaming in of meld u aan bij een account waarvoor livestreaming is ingeschakeld.", + "errorLiveStreamNotEnabled": "Livestreamen is niet ingeschakeld voor {{email}}. Schakel livestreamen in of meld u aan met een account waarbij livestreamen is ingeschakeld.", "expandedOff": "De livestream is gestopt", "expandedOn": "De vergadering wordt momenteel gestreamd naar YouTube.", - "expandedPending": "De livestream wordt gestart…", - "failedToStart": "Livestream niet gestart", - "getStreamKeyManually": "Er zijn geen livestreams opgehaald. Haal de sleutel voor uw livestream op uit YouTube.", - "invalidStreamKey": "De sleutel voor de livestream is mogelijk onjuist.", + "expandedPending": "De livestream wordt gestart...", + "failedToStart": "Livestream starten mislukt", + "getStreamKeyManually": "Er konden geen livestreams opgehaald worden. Probeer uw livestream-sleutel van YouTube te krijgen.", + "invalidStreamKey": "Livestream-sleutel is mogelijk onjuist.", "off": "Livestream gestopt", + "offBy": "{{name}} heeft de livestream gestopt.", "on": "Livestream", - "pending": "Livestream starten…", + "onBy": "{{name}} heeft de livestream gestart.", + "pending": "Livestream starten...", "serviceName": "Livestreamservice", "signedInAs": "U bent momenteel aangemeld als:", - "signIn": "Aanmelden via Google", - "signInCTA": "Meld u aan of voer de sleutel van YouTube voor uw livestream in.", + "signIn": "Aanmelden met Google", + "signInCTA": "Meld u aan of voer uw livestream-sleutel van YouTube in.", "signOut": "Afmelden", "start": "Een livestream starten", "streamIdHelp": "Wat is dit?", - "unavailableTitle": "Livestream niet beschikbaar" + "unavailableTitle": "Livestreamen niet beschikbaar", + "youtubeTerms": "Servicevoorwaarden YouTube", + "googlePrivacyPolicy": "Privacybeleid Google" }, "localRecording": { "clientState": { @@ -378,13 +431,13 @@ "on": "Aan", "unknown": "Onbekend" }, - "dialogTitle": "Besturingselementen voor lokale opnamen", + "dialogTitle": "Besturingselementen voor Lokale Opnamen", "duration": "Duur", "durationNA": "N.v.t.", "encoding": "Codering", - "label": "LOR", + "label": "LOO", "labelToolTip": "Lokale opname is ingeschakeld", - "localRecording": "Lokale opname", + "localRecording": "Lokale Opname", "me": "Ik", "messages": { "engaged": "Lokale opname ingeschakeld.", @@ -401,8 +454,8 @@ "stop": "Opname stoppen", "yes": "Ja" }, - "lockRoomPassword": "", - "lockRoomPasswordUppercase": "", + "lockRoomPassword": "wachtwoord", + "lockRoomPasswordUppercase": "Wachtwoord", "me": "ik", "notify": { "connectedOneMember": "{{name}} neemt nu deel aan de vergadering", @@ -410,94 +463,144 @@ "connectedTwoMembers": "{{first}} en {{second}} nemen nu deel aan de vergadering", "disconnected": "verbinding verbroken", "focus": "Focus van vergadering", - "focusFail": "{{component}} is niet beschikbaar. Probeer het over {{ms}} sec. opnieuw.", - "grantedTo": "Moderatorrechten verleend aan {{to}}.", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "focusFail": "{{component}} niet beschikbaar - probeer over {{ms}} sec. opnieuw", + "grantedTo": "Moderatorrechten verleend aan {{to}}!", + "invitedOneMember": "{{name}} is uitgenodigd", + "invitedThreePlusMembers": "{{name}} en {{count}} anderen zijn uitgenodigd", + "invitedTwoMembers": "{{first}} en {{second}} zijn uitgenodigd", + "kickParticipant": "{{kicked}} is verwijderd door {{kicker}}", "me": "Ik", - "moderator": "Moderatorrechten verleend.", + "moderator": "Moderatorrechten verleend!", "muted": "U hebt het gesprek gedempt gestart.", - "mutedTitle": "U bent gedempt.", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", - "raisedHand": "{{name}} wil spreken.", + "mutedTitle": "U bent gedempt!", + "mutedRemotelyTitle": "U bent gedempt door {{participantDisplayName}}!", + "mutedRemotelyDescription": "U kunt het dempen altijd opheffen wanneer u klaar bent om te spreken. Demp opnieuw wanneer u klaar bent, om ruis buiten de vergadering te houden.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) verwijderd door een andere deelnemer", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) ingesteld door een ander deelnemer", + "raisedHand": "{{name}} zou graag willen spreken.", "somebody": "Iemand", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "Helaas zal uw {{appName}}-ervaring hier niet optimaal zijn. We proberen dit in de toekomst te verbeteren, maar tot die tijd kunt u proberen een van de volledig ondersteunde browsers te gebruiken.", + "startSilentTitle": "U neemt deel zonder audio-output!", + "startSilentDescription": "Neem opnieuw aan de vergadering deel om audio in te schakelen", + "suboptimalBrowserWarning": "Helaas zal uw vergaderervaring hier waarschijnlijk niet zo goed zijn. Er wordt geprobeerd dit in de toekomst te verbeteren, maar tot die tijd kunt u proberen een van de volledig ondersteunde browsers te gebruiken.", "suboptimalExperienceTitle": "Browserwaarschuwing", - "unmute": "", + "unmute": "Dempen opheffen", "newDeviceCameraTitle": "Nieuwe camera gedetecteerd", "newDeviceAudioTitle": "Nieuw audioapparaat gedetecteerd", - "newDeviceAction": "Gebruik" + "newDeviceAction": "Gebruik", + "OldElectronAPPTitle": "Beveiligingskwetsbaarheid!", + "oldElectronClientDescription1": "Het lijkt erop dat u een oude versie van Jitsi Meet gebruikt, waarvan beveiligingskwetsbaarheden bekend zijn. Zorg ervoor dat u nu bijwerkt naar de ", + "oldElectronClientDescription2": "nieuwste versie", + "oldElectronClientDescription3": "!" }, "passwordSetRemotely": "ingesteld door een andere deelnemer", "passwordDigitsOnly": "Maximaal {{number}} cijfers", "poweredby": "mogelijk gemaakt door", + "prejoin": { + "audioAndVideoError": "Audio- en videofout:", + "audioOnlyError": "Audiofout:", + "audioTrackError": "Kon audiotrack niet aanmaken.", + "callMe": "Bel me", + "callMeAtNumber": "Bel me op dit nummer:", + "configuringDevices": "Apparaten instellen...", + "connectedWithAudioQ": "Bent u verbonden met audio?", + "copyAndShare": "Kopieer & deel link naar vergadering", + "dialInMeeting": "Inbellen naar de vergadering", + "dialInPin": "Inbellen naar de vergadering en pincode invoeren:", + "dialing": "Inbellen", + "doNotShow": "Laat dit niet meer zien", + "errorDialOut": "Kon niet bellen", + "errorDialOutDisconnected": "Kon niet bellen. Verbinding verbroken", + "errorDialOutFailed": "Kon niet bellen. Oproep is mislukt", + "errorDialOutStatus": "Fout bij ophalen belstatus", + "errorStatusCode": "Fout bij bellen, statuscode: {{status}}", + "errorValidation": "Nummervalidatie mislukt", + "iWantToDialIn": "Ik wil inbellen", + "joinAudioByPhone": "Deelnemen met telefoonaudio", + "joinMeeting": "Deelnemen aan de vergadering", + "joinWithoutAudio": "Deelnemen zonder audio", + "initiated": "Gesprek gestart", + "linkCopied": "Link gekopieerd naar klembord", + "lookGood": "Het klinkt alsof uw microfoon naar behoren werkt", + "or": "of", + "calling": "Bellen", + "startWithPhone": "Starten met telefoonaudio", + "screenSharingError": "Fout bij schermdeling:", + "videoOnlyError": "Videofout:", + "videoTrackError": "Kon videotrack niet aanmaken.", + "viewAllNumbers": "bekijk alle nummers" + }, "presenceStatus": { "busy": "Bezet", - "calling": "Bellen…", + "calling": "Bellen...", "connected": "Verbonden", - "connecting": "Verbinding maken…", + "connecting": "Verbinding maken...", "connecting2": "Verbinding maken*...", "disconnected": "Verbinding verbroken", "expired": "Verlopen", "ignored": "Genegeerd", - "initializingCall": "Gesprek starten…", + "initializingCall": "Oproep Starten...", "invited": "Uitgenodigd", "rejected": "Geweigerd", - "ringing": "Gaat over…" + "ringing": "Gaat over..." }, "profile": { "setDisplayNameLabel": "Uw weergavenaam instellen", - "setEmailInput": "E-mailadres invoeren", - "setEmailLabel": "Uw Gravatar voor e-mail instellen", + "setEmailInput": "Voer e-mailadres in", + "setEmailLabel": "Uw Gravatar e-mail instellen", "title": "Profiel" }, + "raisedHand": "Zou graag willen spreken", "recording": { + "limitNotificationDescriptionWeb": "Vanwege een grote vraag wordt uw opname beperkt tot {{limit}} min. Voor ongelimiteerde opnamen, probeer {{app}}.", + "limitNotificationDescriptionNative": "Vanwege een grote vraag wordt uw opname beperkt tot {{limit}} min. Voor ongelimiteerde opnamen, probeer <3>{{app}}.", "authDropboxText": "Uploaden naar Dropbox", "availableSpace": "Beschikbare ruimte: {{spaceLeft}} MB (circa {{duration}} minuten aan opname)", "beta": "BÈTA", - "busy": "Er worden opnameresources vrijgemaakt. Probeer het over enkele minuten opnieuw.", - "busyTitle": "Alle opnamefuncties zijn momenteel bezet", + "busy": "Er worden opnamemiddelen vrijgemaakt. Probeer het over enkele minuten opnieuw.", + "busyTitle": "Alle opnemers zijn momenteel bezet", "error": "Opname is mislukt. Probeer het opnieuw.", "expandedOff": "Opname is gestopt", "expandedOn": "De vergadering wordt momenteel opgenomen.", - "expandedPending": "Opname wordt gestart…", - "failedToStart": "Opname starten is mislukt", + "expandedPending": "Opname wordt gestart...", + "failedToStart": "Opname starten mislukt", "fileSharingdescription": "Opname delen met deelnemers aan vergadering", "live": "LIVE", "loggedIn": "Aangemeld als {{userName}}", "off": "Opname gestopt", + "offBy": "{{name}} heeft de opname gestopt", "on": "Opnemen", - "pending": "Voorbereiden op opname van vergadering…", - "rec": "OPN.", + "onBy": "{{name}} heeft de opname gestart", + "pending": "Voorbereiden op opnemen van de vergadering...", + "rec": "OPN", "serviceDescription": "Uw opname wordt opgeslagen door de opnameservice", "serviceName": "Opnameservice", "signIn": "Aanmelden", "signOut": "Afmelden", - "unavailable": "{{serviceName}} is momenteel niet beschikbaar. Er wordt aan een oplossing gewerkt. Probeer het later opnieuw.", + "unavailable": "Oeps! {{serviceName}} is momenteel niet beschikbaar. Er wordt aan een oplossing gewerkt. Probeer het later opnieuw.", "unavailableTitle": "Opname niet beschikbaar" }, "sectionList": { "pullToRefresh": "Naar beneden slepen om te vernieuwen" }, + "security": { + "about": "U kunt een $t(lockRoomPassword) toevoegen aan uw vergadering. Deelnemers moeten het $t(lockRoomPassword) opgeven voordat zij aan de vergadering mogen deelnemen.", + "aboutReadOnly": "Moderators kunnen een $t(lockRoomPassword) toevoegen aan de vergadering. Deelnemers moeten het $t(lockRoomPassword) opgeven voordat zij aan de vergadering mogen deelnemen.", + "insecureRoomNameWarning": "De naam van de ruimte is onveilig. Ongewenste deelnemers kunnen deelnemen aan uw vergadering. Overweeg uw vergadering te beveiligen via de beveiligingsknop.", + "securityOptions": "Beveiligingsopties" + }, "settings": { "calendar": { - "about": "De agenda-integratie van {{appName}} wordt gebruikt voor een veilige toegang tot uw agenda, zodat geplande gebeurtenissen kunnen worden geraadpleegd.", + "about": "De agenda-integratie van {{appName}} wordt gebruikt voor een veilige toegang tot uw agenda, zodat het aankomende afspraken kan uitlezen.", "disconnect": "Verbinding verbreken", - "microsoftSignIn": "Aanmelden via Microsoft", - "signedIn": "Agendagebeurtenissen voor {{email}} worden geraadpleegd. Klik op de knop 'Verbinding verbreken' hieronder om de toegang tot agendagebeurtenissen in te trekken.", + "microsoftSignIn": "Aanmelden met Microsoft", + "signedIn": "Agenda-afspraken voor {{email}} worden uitgelezen. Klik op de knop 'Verbinding verbreken' hieronder om de toegang tot agenda-afspraken te stoppen.", "title": "Agenda" }, "devices": "Apparaten", "followMe": "Iedereen volgt mij", "language": "Taal", "loggedIn": "Aangemeld als {{name}}", + "microphones": "Microfoons", "moderator": "Moderator", "more": "Meer", "name": "Naam", @@ -505,6 +608,7 @@ "selectAudioOutput": "Audio-uitvoer", "selectCamera": "Camera", "selectMic": "Microfoon", + "speakers": "Speakers", "startAudioMuted": "Iedereen start gedempt", "startVideoMuted": "Iedereen start verborgen", "title": "Instellingen" @@ -512,30 +616,35 @@ "settingsView": { "advanced": "Geavanceerd", "alertOk": "OK", + "alertCancel": "Annuleren", "alertTitle": "Waarschuwing", "alertURLText": "De ingevoerde server-URL is ongeldig", "buildInfoSection": "Versiegegevens", - "conferenceSection": "Bijeenkomsten", + "conferenceSection": "Vergadering", + "disableCallIntegration": "Schakel inbellen via telefoon uit", + "disableP2P": "Schakel Peer-to-Peer-modus uit", + "disableCrashReporting": "Schakel crashrapportage uit", + "disableCrashReportingWarning": "Weet u zeker dat u crashrapportage wilt uitschakelen? De instelling wordt toegepast nadat u de app opnieuw hebt opgestart.", "displayName": "Weergavenaam", "email": "E‑mail", "header": "Instellingen", "profileSection": "Profiel", "serverURL": "Server-URL", - "showAdvanced": "Toon geavanceerde instellingen", - "startWithAudioMuted": "Starten met audio gedempt", - "startWithVideoMuted": "Starten met video gedempt", + "showAdvanced": "Geavanceerde instellingen weergeven", + "startWithAudioMuted": "Met audio gedempt starten", + "startWithVideoMuted": "Met video gedempt starten", "version": "Versie" }, "share": { - "dialInfoText": "", + "dialInfoText": "\n\n=====\n\nWilt u inbellen via uw telefoon?\n\n{{defaultDialInNumber}}Klik op deze link om de inbelnummers voor deze vergadering te zien\n{{dialInfoPageUrl}}", "mainText": "Klik op de volgende link om deel te nemen aan de vergadering:\n{{roomUrl}}" }, "speaker": "Spreker", "speakerStats": { - "hours": "{{count}} u", - "minutes": "{{count}} m", + "hours": "{{count}}u", + "minutes": "{{count}}m", "name": "Naam", - "seconds": "{{count}} s", + "seconds": "{{count}}s", "speakerStats": "Sprekerstatistieken", "speakerTime": "Sprekertijd" }, @@ -546,179 +655,246 @@ "suspendedoverlay": { "rejoinKeyTitle": "Opnieuw deelnemen", "text": "Druk op de knop Opnieuw deelnemen om opnieuw verbinding te maken.", - "title": "Uw videogesprek is onderbroken, omdat de slaapstand van de computer is geactiveerd." + "title": "Uw videogesprek is onderbroken omdat deze computer op de slaapstand overging." }, "toolbar": { "accessibilityLabel": { - "audioOnly": "Alleen audio schakelen", + "audioOnly": "Alleen audio in- of uitschakelen", "audioRoute": "Het afspeelapparaat selecteren", - "callQuality": "Kwaliteit van gesprek beheren", - "cc": "Ondertiteling schakelen", - "chat": "Chatvenster schakelen", - "document": "Gedeeld document schakelen", + "callQuality": "Videokwaliteit beheren", + "cc": "Ondertiteling in- of uitschakelen", + "chat": "Chatvenster in- of uitschakelen", + "document": "Gedeeld document in- of uitschakelen", + "download": "Download onze apps", + "e2ee": "Eind-tot-eind-versleuteling", "feedback": "Feedback achterlaten", - "fullScreen": "Volledig scherm schakelen", + "fullScreen": "Volledig scherm in- of uitschakelen", "hangup": "Het gesprek verlaten", + "help": "Hulp", "invite": "Personen uitnodigen", "kick": "Deelnemer verwijderen", - "localRecording": "Besturingselementen voor lokale opname schakelen", - "lockRoom": "Wachtwoord voor vergadering schakelen", - "moreActions": "Menu 'Meer acties' schakelen", - "moreActionsMenu": "Menu 'Meer acties'", - "mute": "Audio dempen schakelen", - "pip": "Beeld-in-beeld-modus schakelen", + "lobbyButton": "Lobby-modus in- of uitschakelen", + "localRecording": "Besturingselementen voor lokale opname in- of uitschakelen", + "lockRoom": "Wachtwoord voor vergadering in- of uitschakelen", + "moreActions": "Meer acties menu in- of uitschakelen", + "moreActionsMenu": "Meer acties menu", + "moreOptions": "Meer opties weergeven", + "mute": "Audio dempen in- of uitschakelen", + "muteEveryone": "Iedereen dempen", + "pip": "Picture-in-Picture-modus in- of uitschakelen", + "privateMessage": "Verstuur privébericht", "profile": "Uw profiel bewerken", - "raiseHand": "Hand opsteken schakelen", - "recording": "Opname schakelen", + "raiseHand": "Hand opsteken in- of uitschakelen", + "recording": "Opnemen in- of uitschakelen", "remoteMute": "Deelnemer dempen", - "Settings": "Instellingen schakelen", - "sharedvideo": "YouTube-video delen schakelen", + "security": "Beveiligingsopties", + "Settings": "Instellingen in- of uitschakelen", + "sharedvideo": "YouTube-video delen in- of uitschakelen", "shareRoom": "Iemand uitnodigen", - "shareYourScreen": "Scherm delen schakelen", - "shortcuts": "Sneltoetsen schakelen", - "show": "", - "speakerStats": "Sprekerstatistieken schakelen", - "tileView": "Tegelweergave schakelen", - "toggleCamera": "Camera schakelen", - "videomute": "Video dempen schakelen", - "videoblur": "" + "shareYourScreen": "Schermdeling in- of uitschakelen", + "shortcuts": "Sneltoetsen in- of uitschakelen", + "show": "Op podium weergeven", + "speakerStats": "Sprekerstatistieken in- of uitschakelen", + "tileView": "Tegelweergave in- of uitschakelen", + "toggleCamera": "Camera in- of uitschakelen", + "toggleFilmstrip": "Filmstrip in- of uitschakelen", + "videomute": "Video dempen in- of uitschakelen", + "videoblur": "Video vervagen in- of uitschakelen" }, "addPeople": "Personen aan uw gesprek toevoegen", - "audioOnlyOff": "Modus 'Alleen audio' uitschakelen", - "audioOnlyOn": "Modus 'Alleen audio' inschakelen", + "audioOnlyOff": "Lage bandbreedte-modus uitschakelen", + "audioOnlyOn": "Lage bandbreedte-modus inschakelen", "audioRoute": "Het afspeelapparaat selecteren", - "authenticate": "Verifiëren", - "callQuality": "Kwaliteit van gesprek beheren", - "chat": "Chat openen/sluiten", + "authenticate": "Authenticeren", + "callQuality": "Videokwaliteit beheren", + "chat": "Chat openen / sluiten", "closeChat": "Chat sluiten", "documentClose": "Gedeeld document sluiten", "documentOpen": "Gedeeld document openen", + "download": "Download onze apps", + "e2ee": "Eind-tot-eind-versleuteling", "enterFullScreen": "Volledig scherm weergeven", "enterTileView": "Tegelweergave openen", "exitFullScreen": "Volledig scherm sluiten", "exitTileView": "Tegelweergave sluiten", "feedback": "Feedback achterlaten", "hangup": "Verlaten", + "help": "Hulp", "invite": "Personen uitnodigen", + "lobbyButtonDisable": "Schakel lobby-modus uit", + "lobbyButtonEnable": "Schakel lobby-modus in", "login": "Aanmelden", "logout": "Afmelden", "lowerYourHand": "Uw hand laten zakken", "moreActions": "Meer acties", "moreOptions": "Meer opties", - "mute": "Dempen/dempen opheffen", + "mute": "Dempen / Dempen opheffen", "muteEveryone": "Iedereen dempen", + "noAudioSignalTitle": "Er komt geen invoer van uw microfoon!", + "noAudioSignalDesc": "Als u niet met opzet hebt gedempt vanuit systeeminstellingen of hardware, overweeg dan van apparaat te wisselen.", + "noAudioSignalDescSuggestion": "Als u niet met opzet hebt gedempt vanuit systeeminstellingen of hardware, overweeg dan over te schakelen naar het gesuggereerde apparaat.", + "noAudioSignalDialInDesc": "U kunt ook inbellen met:", + "noAudioSignalDialInLinkDesc": "Inbelnummers", + "noisyAudioInputTitle": "Uw microfoon lijkt ruis te veroorzaken!", + "noisyAudioInputDesc": "Het klinkt alsof uw microfoon ruis veroorzaakt. Overweeg te dempen of van apparaat te wisselen.", "openChat": "Chat openen", - "pip": "Beeld-in-beeld-modus activeren", + "pip": "Picture-in-Picture-modus activeren", + "privateMessage": "Verstuur privébericht", "profile": "Uw profiel bewerken", - "raiseHand": "Uw hand opsteken/laten zakken", + "raiseHand": "Uw hand opsteken / laten zakken", "raiseYourHand": "Uw hand opsteken", + "security": "Beveiligingsoptions", "Settings": "Instellingen", "sharedvideo": "Een YouTube-video delen", "shareRoom": "Iemand uitnodigen", "shortcuts": "Sneltoetsen weergeven", "speakerStats": "Sprekerstatistieken", - "startScreenSharing": "Scherm delen starten", + "startScreenSharing": "Schermdeling starten", "startSubtitles": "Ondertiteling starten", - "stopScreenSharing": "Scherm delen stoppen", + "stopScreenSharing": "Schermdeling stoppen", "stopSubtitles": "Ondertiteling stoppen", "stopSharedVideo": "YouTube-video stoppen", - "talkWhileMutedPopup": "Wilt u spreken? U bent gedempt.", - "tileViewToggle": "Tegelweergave schakelen", - "toggleCamera": "Camera schakelen", - "videomute": "Camera starten/stoppen", - "startvideoblur": "", - "stopvideoblur": "" + "talkWhileMutedPopup": "Probeert u te spreken? U bent gedempt.", + "tileViewToggle": "Tegelweergave in- of uitschakelen", + "toggleCamera": "Camera in- of uitschakelen", + "videomute": "Camera starten / stoppen", + "startvideoblur": "Vervaag mijn achtergrond", + "stopvideoblur": "Achtergrond vervagen uitschakelen" }, "transcribing": { - "ccButtonTooltip": "Ondertiteling starten/stoppen", + "ccButtonTooltip": "Ondertiteling starten / stoppen", "error": "Transcriberen is mislukt. Probeer het opnieuw.", "expandedLabel": "Transcriberen is momenteel ingeschakeld", - "failedToStart": "Transcriberen starten is mislukt", + "failedToStart": "Transcriberen starten mislukt", "labelToolTip": "De vergadering wordt getranscribeerd", "off": "Transcriberen gestopt", - "pending": "Voorbereiden op transcriberen van vergadering…", + "pending": "Voorbereiden op transcriberen van de vergadering...", "start": "Weergave van ondertiteling starten", "stop": "Weergave van ondertiteling stoppen", "tr": "TR" }, "userMedia": { - "androidGrantPermissions": "Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.", - "chromeGrantPermissions": "Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.", - "edgeGrantPermissions": "Selecteer Ja wanneer u in de browser om machtigingen wordt gevraagd.", - "electronGrantPermissions": "Verleen machtigingen voor het gebruik van uw camera en microfoon", - "firefoxGrantPermissions": "Selecteer Geselecteerd apparaat delen wanneer u in de browser om machtigingen wordt gevraagd.", - "iexplorerGrantPermissions": "Selecteer OK wanneer u in de browser om machtigingen wordt gevraagd.", - "nwjsGrantPermissions": "Verleen machtigingen voor het gebruik van uw camera en microfoon", - "operaGrantPermissions": "Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.", - "react-nativeGrantPermissions": "Selecteer Toestaan wanneer u in de browser om machtigingen wordt gevraagd.", - "safariGrantPermissions": "Selecteer OK wanneer u in de browser om machtigingen wordt gevraagd." + "androidGrantPermissions": "Selecteer Toestaan wanneer uw browser om toegangsrechten vraagt.", + "chromeGrantPermissions": "Selecteer Toestaan wanneer uw browser om toegangsrechten vraagt.", + "edgeGrantPermissions": "Selecteer Ja wanneer uw browser om toegangsrechten vraagt.", + "electronGrantPermissions": "Verleen toestemming om uw camera en microfoon te gebruiken.", + "firefoxGrantPermissions": "Selecteer Geselecteerd apparaat delen wanneer uw browser om toegangsrechten vraagt.", + "iexplorerGrantPermissions": "Selecteer OK wanneer uw browser om toegangsrechten vraagt.", + "nwjsGrantPermissions": "Verleen toestemming om uw camera en microfoon te gebruiken.", + "operaGrantPermissions": "Selecteer Toestaan wanneer uw browser om toegangsrechten vraagt.", + "react-nativeGrantPermissions": "Selecteer Toestaan wanneer uw browser om toegangsrechten vraagt.", + "safariGrantPermissions": "Selecteer OK wanneer uw browser om toegangsrechten vraagt." }, "videoSIPGW": { - "busy": "Er worden resources vrijgemaakt. Probeer het over enkele minuten opnieuw.", - "busyTitle": "De ruimteservice is momenteel bezet", + "busy": "Er worden middelen vrijgemaakt. Probeer het over enkele minuten opnieuw.", + "busyTitle": "De Ruimteservice is momenteel bezet", "errorAlreadyInvited": "{{displayName}} is al uitgenodigd", - "errorInvite": "Vergadering is nog niet van start gegaan. Probeer het later opnieuw.", + "errorInvite": "Vergadering nog niet tot stand gebracht. Probeer het later opnieuw.", "errorInviteFailed": "Er wordt aan een oplossing gewerkt. Probeer het later opnieuw.", - "errorInviteFailedTitle": "Het uitnodigen van {{displayName}} is mislukt", - "errorInviteTitle": "Fout bij uitnodiging voor ruimte", + "errorInviteFailedTitle": "Uitnodigen van {{displayName}} is mislukt", + "errorInviteTitle": "Fout bij uitnodigen ruimte", "pending": "{{displayName}} is uitgenodigd" }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "De modus 'Alleen audio' is geactiveerd. In deze modus wordt er bandbreedte bespaard, maar ziet u geen video's van anderen.", - "callQuality": "Kwaliteit van gesprek", + "audioOnlyExpanded": "U bent in lage bandbreedte-modus. In deze modus ontvangt u alleen audio en schermdeling.", + "callQuality": "Videokwaliteit", "hd": "HD", + "hdTooltip": "U bekijkt video in hoge resolutie", "highDefinition": "Hoge resolutie", "labelTooiltipNoVideo": "Geen video", - "labelTooltipAudioOnly": "Modus 'Alleen audio' ingeschakeld", + "labelTooltipAudioOnly": "Lage bandbreedte-modus ingeschakeld", "ld": "LD", + "ldTooltip": "U bekijkt video in lage resolutie", "lowDefinition": "Lage resolutie", "onlyAudioAvailable": "Alleen audio is beschikbaar", "onlyAudioSupported": "In deze browser wordt alleen audio ondersteund.", "sd": "SD", - "standardDefinition": "Standaardresolutie" + "sdTooltip": "U bekijkt video in standaard resolutie", + "standardDefinition": "Standaard resolutie" }, "videothumbnail": { "domute": "Dempen", - "flip": "Omslaan", + "domuteOthers": "Alle anderen dempen", + "flip": "Omdraaien", "kick": "Verwijderen", "moderator": "Moderator", "mute": "Deelnemer is gedempt", "muted": "Gedempt", - "remoteControl": "Extern beheer", - "show": "", + "remoteControl": "Extern beheer starten / stoppen", + "show": "Op podium weergeven", "videomute": "Deelnemer heeft de camera gestopt" }, "welcomepage": { "accessibilityLabel": { "join": "Tik om deel te nemen", - "roomname": "Naam van ruimte invoeren" + "roomname": "Voer naam van ruimte in" }, - "appDescription": "U kunt nu videochatten met het hele team. U kunt uitnodigen wie u maar wilt. {{app}} is een volledig versleutelde, 100% open-sourceoplossing voor videovergaderingen die u wanneer en zo lang u maar wilt gratis kunt gebruiken. Hier hebt u geen account voor nodig.", + "appDescription": "U kunt nu videochatten met het gehele team. Nodig uit wie u maar wilt. {{app}} is een volledig versleutelde, 100% open-source oplossing voor videovergaderingen, die u wanneer u maar wilt gratis kunt gebruiken — zonder dat u een account nodig hebt.", "audioVideoSwitch": { "audio": "Spraak", "video": "Video" }, "calendar": "Agenda", "connectCalendarButton": "Uw agenda koppelen", - "connectCalendarText": "", + "connectCalendarText": "Koppel uw agenda om al uw vergaderingen in {{app}} te bekijken. Voeg bovendien {{provider}}-vergaderingen toe aan uw agenda en start ze met één klik.", "enterRoomTitle": "Een nieuwe vergadering starten", - "go": "Start", - "join": "Deelnemen", + "getHelp": "Hulp krijgen", + "go": "GAAN", + "goSmall": "GAAN", "info": "Informatie", + "join": "AANMAKEN / DEELNEMEN", + "moderatedMessage": "Of boek een vergadering URL van tevoren waar u de enige moderator bent.", "privacy": "Privacy", "recentList": "Recent", "recentListDelete": "Verwijderen", - "recentListEmpty": "Uw lijst met recente items is momenteel leeg. Als u chat met uw team, worden alle recente vergaderingen hier weergegeven.", - "reducedUIText": "", - "roomname": "Naam van ruimte invoeren", - "roomnameHint": "Voer de naam of URL in van de ruimte die u wilt betreden. U kunt een naam verzinnen, maar geef de naam wel door aan de andere deelnemers, zodat zij dezelfde naam kunnen invoeren.", - "sendFeedback": "Feedback sturen", + "recentListEmpty": "Uw recent lijst is momenteel leeg. Wanneer u chat met uw team, vindt u hier al uw recente vergaderingen.", + "reducedUIText": "Welkom in {{app}}!", + "roomNameAllowedChars": "Naam van vergadering mag geen van deze tekens bevatten: ?, &, :, ', \", %, #.", + "roomname": "Voer naam van ruimte in", + "roomnameHint": "Voer de naam of URL in van de ruimte die u wilt betreden. U kunt een naam verzinnen, maar laat deze wel weten aan de andere deelnemers, zodat zij dezelfde naam invoeren.", + "sendFeedback": "Feedback versturen", "terms": "Voorwaarden", "title": "Veilige, volledig uitgeruste en geheel gratis videovergaderingen" }, "lonelyMeetingExperience": { "button": "Anderen uitnodigen", - "youAreAlone": "Je bent de enige in dit gesprek" + "youAreAlone": "U bent de enige in de vergadering" + }, + "helpView": { + "header": "Helpcentrum" + }, + "lobby": { + "knockingParticipantList" : "Lijst van aankloppende deelnemers", + "allow": "Toestaan", + "backToKnockModeButton": "Geen wachtwoord, vraag om deel te mogen nemen", + "dialogTitle": "Lobby-modus", + "disableDialogContent": "Lobby-modus is momenteel ingeschakeld. Deze functie zorgt ervoor dat ongewenste deelnemers niet aan uw vergadering kunnen deelnemen. Wilt u het uitschakelen?", + "disableDialogSubmit": "Uitschakelen", + "emailField": "Voer uw e-mailadres in", + "enableDialogPasswordField": "Stel wachtwoord in (optioneel)", + "enableDialogSubmit": "Inschakelen", + "enableDialogText": "Met de lobby-modus kunt u uw vergadering beveiligen, door deelnemers alleen toe te laten na een formele goedkeuring van een moderator.", + "enterPasswordButton": "Voer wachtwoord voor vergadering in", + "enterPasswordTitle": "Voer wachtwoord in om deel te nemen aan vergadering", + "invalidPassword": "Ongeldig wachtwoord", + "joiningMessage": "U neemt deel aan de vergadering zodra iemand uw verzoek accepteert", + "joinWithPasswordMessage": "Poging tot deelname met wachtwoord, even geduld a.u.b...", + "joinRejectedMessage": "Uw verzoek tot deelname is afgewezen door een moderator.", + "joinTitle": "Deelnemen aan vergadering", + "joiningTitle": "Vragen om deel te nemen aan vergadering...", + "joiningWithPasswordTitle": "Deelnemen met wachtwoord...", + "knockButton": "Vragen om deel te nemen", + "knockTitle": "Iemand wil deelnemen aan de vergadering", + "nameField": "Voer uw naam in", + "notificationLobbyAccessDenied": "{{targetParticipantName}} is afgewezen om deel te nemen door {{originParticipantName}}", + "notificationLobbyAccessGranted": "{{targetParticipantName}} is toegestaan om deel te nemen door {{originParticipantName}}", + "notificationLobbyDisabled": "Lobby is uitgeschakeld door {{originParticipantName}}", + "notificationLobbyEnabled": "Lobby is ingeschakeld door {{originParticipantName}}", + "notificationTitle": "Lobby", + "passwordField": "Voer wachtwoord voor vergadering in", + "passwordJoinButton": "Deelnemen", + "reject": "Afwijzen", + "toggleLabel": "Lobby inschakelen" } } From fcc69b92bb23ac3f85e40e9c294e0ed694f5c5cb Mon Sep 17 00:00:00 2001 From: paweldomas Date: Tue, 14 Jul 2020 11:47:32 -0500 Subject: [PATCH 060/167] deps: update LJM to bring in new audio levels and e2ee tweaks Updates LJM to ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 44e941b56..ead52310b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10725,8 +10725,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#cd008d726f1f57562eb5d8e6a3cd91c7e69826a0", - "from": "github:jitsi/lib-jitsi-meet#cd008d726f1f57562eb5d8e6a3cd91c7e69826a0", + "version": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", + "from": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", "requires": { "@jitsi/js-utils": "1.0.0", "@jitsi/sdp-interop": "1.0.3", diff --git a/package.json b/package.json index 51d98c46c..8dfbd9022 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "jquery-i18next": "1.2.1", "js-md5": "0.6.1", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#304b0a2b4e18216d792f499c74fc24bc3849303e", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.13", "moment": "2.19.4", From 758b60f92b548a21b3a46d671ccea199fb0e9738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Tue, 14 Jul 2020 13:16:53 -0500 Subject: [PATCH 061/167] fix: Updates coturn config on update. (#7306) * fix: Updates coturn config on update. * fix: Updates console message. --- debian/jitsi-meet-turnserver.postinst | 29 +++++++++++++++++++++- doc/debian/jitsi-meet-turn/turnserver.conf | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/debian/jitsi-meet-turnserver.postinst b/debian/jitsi-meet-turnserver.postinst index e3ca04752..ecb5bc03a 100644 --- a/debian/jitsi-meet-turnserver.postinst +++ b/debian/jitsi-meet-turnserver.postinst @@ -87,9 +87,36 @@ case "$1" in if [[ -f $TURN_CONFIG ]] ; then echo "------------------------------------------------" echo "" - echo "turnserver is already configured on this machine, skipping." + echo "turnserver is already configured on this machine." echo "" echo "------------------------------------------------" + + if grep -q "jitsi-meet coturn config" "$TURN_CONFIG" && ! grep -q "jitsi-meet coturn relay disable config" "$TURN_CONFIG" ; then + echo "Updating coturn config" + echo "# jitsi-meet coturn relay disable config. Do not modify this line +no-multicast-peers +no-cli +no-loopback-peers +no-tcp-relay +denied-peer-ip=0.0.0.0-0.255.255.255 +denied-peer-ip=10.0.0.0-10.255.255.255 +denied-peer-ip=100.64.0.0-100.127.255.255 +denied-peer-ip=127.0.0.0-127.255.255.255 +denied-peer-ip=169.254.0.0-169.254.255.255 +denied-peer-ip=127.0.0.0-127.255.255.255 +denied-peer-ip=172.16.0.0-172.31.255.255 +denied-peer-ip=192.0.0.0-192.0.0.255 +denied-peer-ip=192.0.2.0-192.0.2.255 +denied-peer-ip=192.88.99.0-192.88.99.255 +denied-peer-ip=192.168.0.0-192.168.255.255 +denied-peer-ip=198.18.0.0-198.19.255.255 +denied-peer-ip=198.51.100.0-198.51.100.255 +denied-peer-ip=203.0.113.0-203.0.113.255 +denied-peer-ip=240.0.0.0-255.255.255.255" >> $TURN_CONFIG + + invoke-rc.d coturn restart || true + fi + db_stop exit 0 fi diff --git a/doc/debian/jitsi-meet-turn/turnserver.conf b/doc/debian/jitsi-meet-turn/turnserver.conf index 57ae23e35..02dff699b 100644 --- a/doc/debian/jitsi-meet-turn/turnserver.conf +++ b/doc/debian/jitsi-meet-turn/turnserver.conf @@ -17,6 +17,7 @@ no-tlsv1 no-tlsv1_1 # https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4 cipher-list=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 +# jitsi-meet coturn relay disable config. Do not modify this line denied-peer-ip=0.0.0.0-0.255.255.255 denied-peer-ip=10.0.0.0-10.255.255.255 denied-peer-ip=100.64.0.0-100.127.255.255 From 41ba55a6a93b2e2feff433e80770fb1fb2a78f28 Mon Sep 17 00:00:00 2001 From: NicolasD Date: Wed, 15 Jul 2020 10:22:35 +0200 Subject: [PATCH 062/167] rn,flags: add feature flag to enable / disable conference timer --- react/features/base/flags/constants.js | 6 ++++++ .../conference/components/native/NavigationBar.js | 12 ++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/react/features/base/flags/constants.js b/react/features/base/flags/constants.js index 7592cc901..2d3e2dd58 100644 --- a/react/features/base/flags/constants.js +++ b/react/features/base/flags/constants.js @@ -25,6 +25,12 @@ export const CALL_INTEGRATION_ENABLED = 'call-integration.enabled'; */ export const CLOSE_CAPTIONS_ENABLED = 'close-captions.enabled'; +/** + * Flag indicating if conference timer should be enabled. + * Default: enabled (true). + */ +export const CONFERENCE_TIMER_ENABLED = 'conference-timer.enabled'; + /** * Flag indicating if chat should be enabled. * Default: enabled (true). diff --git a/react/features/conference/components/native/NavigationBar.js b/react/features/conference/components/native/NavigationBar.js index e35ff440b..7088875ac 100644 --- a/react/features/conference/components/native/NavigationBar.js +++ b/react/features/conference/components/native/NavigationBar.js @@ -5,7 +5,7 @@ import { SafeAreaView, Text, View } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { getConferenceName } from '../../../base/conference'; -import { getFeatureFlag, MEETING_NAME_ENABLED } from '../../../base/flags'; +import { getFeatureFlag, CONFERENCE_TIMER_ENABLED, MEETING_NAME_ENABLED } from '../../../base/flags'; import { connect } from '../../../base/redux'; import { PictureInPictureButton } from '../../../mobile/picture-in-picture'; import { isToolboxVisible } from '../../../toolbox'; @@ -15,6 +15,11 @@ import styles, { NAVBAR_GRADIENT_COLORS } from './styles'; type Props = { + /** + * Whether displaying the current conference timer is enabled or not. + */ + _conferenceTimerEnabled: boolean, + /** * Name of the meeting we're currently in. */ @@ -73,7 +78,9 @@ class NavigationBar extends Component { { this.props._meetingName } } - + { + this.props._conferenceTimerEnabled && + } ]; @@ -89,6 +96,7 @@ class NavigationBar extends Component { */ function _mapStateToProps(state) { return { + _conferenceTimerEnabled: getFeatureFlag(state, CONFERENCE_TIMER_ENABLED, true), _meetingName: getConferenceName(state), _meetingNameEnabled: getFeatureFlag(state, MEETING_NAME_ENABLED, true), _visible: isToolboxVisible(state) From e34c5673b26e3be0baccc96ebd7bbd3c3d1b9339 Mon Sep 17 00:00:00 2001 From: Mutusen <47037331+Mutusen@users.noreply.github.com> Date: Wed, 15 Jul 2020 10:24:10 +0200 Subject: [PATCH 063/167] lang: update Esperanto translation --- lang/main-eo.json | 878 +++++++++++++++++++++++++--------------------- 1 file changed, 485 insertions(+), 393 deletions(-) diff --git a/lang/main-eo.json b/lang/main-eo.json index cbdcf18ff..c9dff5fbb 100644 --- a/lang/main-eo.json +++ b/lang/main-eo.json @@ -1,60 +1,71 @@ { "addPeople": { - "add": "", - "countryNotSupported": "", - "countryReminder": "", - "disabled": "", - "failedToAdd": "", - "footerText": "", - "loading": "", - "loadingNumber": "", - "loadingPeople": "", + "add": "Inviti", + "countryNotSupported": "Ni ankoraŭ ne subtenas ĉi tiun landon.", + "countryReminder": "Ĉu vi vokas ekster Usonon? Certiĝu, ke vi komencas kun la landokodo!", + "disabled": "Vi ne povas inviti homojn.", + "failedToAdd": "Malsukcesis aldono de membroj", + "footerText": "Alvokoj estas malŝaltitaj.", + "loading": "Serĉado de homoj kaj telefonnumeroj", + "loadingNumber": "Validigo de telefonnumero", + "loadingPeople": "Serĉado de homoj por inviti", "noResults": "Nenio trovita", - "noValidNumbers": "", - "searchNumbers": "", - "searchPeople": "", - "searchPeopleAndNumbers": "", - "telephone": "", - "title": "" + "noValidNumbers": "Bonvolu entajpi telefonnumeron", + "searchNumbers": "Aldoni telefonnumerojn", + "searchPeople": "Serĉi homojn", + "searchPeopleAndNumbers": "Serĉi homojn aŭ aldoni iliajn telefonnumerojn", + "telephone": "Telefono: {{number}}", + "title": "Inviti homojn al ĉi tiu kunveno" }, "audioDevices": { "bluetooth": "Bludento", "headphones": "Kapaŭskultiloj", "phone": "Telefono", - "speaker": "Parolanto" + "speaker": "Parolanto", + "none": "Neniu disponebla sonaparato" }, "audioOnly": { - "audioOnly": "Nur sono" + "audioOnly": "Malalta rapideco de retkonekto" }, "calendarSync": { - "addMeetingURL": "", - "confirmAddLink": "", + "addMeetingURL": "Aldoni ligilon al la kunveno", + "confirmAddLink": "Ĉu vi volas aldoni Jitsi-ligilon al ĉi tiu evento?", "error": { - "appConfiguration": "", - "generic": "", - "notSignedIn": "" + "appConfiguration": "Kalendara integrigo ne estas ĝuste agordita.", + "generic": "Okazis eraro. Bonvolu kontroli viajn kalendarajn agordojn aŭ provu aktualigi la kalendaron.", + "notSignedIn": "Okazis eraro dum aŭtentigado por vidi kalendarajn eventojn. Bonvolu kontroli viajn kalendarajn agordojn kaj provi ensaluti denove." }, - "join": "", - "joinTooltip": "", - "nextMeeting": "", - "noEvents": "", - "ongoingMeeting": "", - "permissionButton": "", - "permissionMessage": "", - "refresh": "", - "today": "" + "join": "Aliĝi", + "joinTooltip": "Aliĝi al la kunveno", + "nextMeeting": "sekva kunveno", + "noEvents": "Ne estas planitaj eventoj..", + "ongoingMeeting": "okazanta kunveno", + "permissionButton": "Malfermi agordojn", + "permissionMessage": "Kalendara permeso estas bezonata, por ke vi povu vidi viajn kunvenojn en la aplikaĵo.", + "refresh": "Aktualigi la kalendaron", + "today": "Hodiaŭ" }, "chat": { - "error": "", - "messagebox": "", + "error": "Eraro: via mesaĝo “{{originalText}}” ne estis sendita. Kialo: {{error}}", + "fieldPlaceHolder": "Tajpu vian mesaĝon ĉi tien", + "messagebox": "Tajpu mesaĝon", + "messageTo": "Privata mesaĝo al {{recipient}}", + "noMessagesMessage": "Ankoraŭ ne estas mesaĝoj en la kunveno. Komencu konversation ĉi tie!", "nickname": { "popover": "Elektu kaŝnomon", - "title": "" + "title": "Elektu kaŝnomon por uzi la babilejon" }, - "title": "" + "privateNotice": "Privata mesaĝo al {{recipient}}", + "title": "Babilejo", + "you": "vi" + }, + "chromeExtensionBanner": { + "installExtensionText": "Instali la kromprogramon por integrigo de Google Calendar kaj Office 365", + "buttonText": "Instali kromprogramon por Chrome", + "dontShowAgain": "Ne plu montru tion al mi" }, "connectingOverlay": { - "joiningRoom": "" + "joiningRoom": "Konektiĝo al via kunveno…" }, "connection": { "ATTACHED": "Kunligita", @@ -66,22 +77,26 @@ "DISCONNECTED": "Malkonektita", "DISCONNECTING": "Malkonektanta", "ERROR": "Eraro", - "RECONNECTING": "Reta eraro okazis. Rekonektanta..." + "FETCH_SESSION_ID": "Ricevado de session-id…", + "GET_SESSION_ID_ERROR": "Eraro dum ricevado de session-id: {{code}}", + "GOT_SESSION_ID": "Ricevado de session-id… farita", + "LOW_BANDWIDTH": "Video por {{displayName}} estis malŝaltita por ŝpari retan trafikon" }, "connectionindicator": { - "address": "Adreso:", - "bandwidth": "Antaŭkalkulita kapacito:", - "bitrate": "Bitrapido:", - "bridgeCount": "", - "connectedTo": "", - "framerate": "Bildrapido:", - "less": "Montri malpli", - "localaddress": "Loka adreso:", - "localaddress_plural": "Lokaj adresoj:", - "localport": "Loka pordo:", - "localport_plural": "Lokaj pordoj:", - "more": "Motri pli", - "packetloss": "Paketperdo:", + "address": "Address:", + "bandwidth": "Estimated bandwidth:", + "bitrate": "Bitrate:", + "bridgeCount": "Server count: ", + "connectedTo": "Connected to:", + "e2e_rtt": "E2E RTT:", + "framerate": "Frame rate:", + "less": "Show less", + "localaddress": "Local address:", + "localaddress_plural": "Local addresses:", + "localport": "Local port:", + "localport_plural": "Local ports:", + "more": "Show more", + "packetloss": "Packet loss:", "quality": { "good": "Bona", "inactive": "Neaktiva", @@ -96,24 +111,25 @@ "resolution": "Distingivo:", "status": "Konekto:", "transport": "Transporto:", - "turn": " (truni)" + "transport_plural": "Transportoj:" }, "dateUtils": { - "earlier": "", - "today": "", - "yesterday": "" + "earlier": "Pli frue", + "today": "Hodiaŭ", + "yesterday": "Hieraŭ" }, "deepLinking": { - "appNotInstalled": "", - "description": "", - "descriptionWithoutWeb": "", + "appNotInstalled": "Vi bezonas la aplikaĵon {{app}} por aliĝi al ĉi tiu kunveno per via telefono.", + "description": "Ĉu nenio okazis? Ni provis lanĉi vian kunveno en la komputila aplikaĵo {{app}}. Provu denove aŭ lanĉu ĝin en la reta aplikaĵo {{web}}.", + "descriptionWithoutWeb": "Ĉu nenio okazis? Ni provis lanĉi vian kunveno en la komputila aplikaĵo {{app}}.", "downloadApp": "Elŝuti la aplikaĵon", - "launchWebButton": "", - "openApp": "", - "title": "", - "tryAgainButton": "" + "launchWebButton": "Lanĉi enrete", + "openApp": "Iri plu al la aplikaĵo", + "title": "Lanĉo de via kunveno en {{app}}…", + "tryAgainButton": "Provu denove per la komputila aplikaĵo" }, "defaultLink": "ekz. {{url}}", + "defaultNickname": "ekz. Johano Verda", "deviceError": { "cameraError": "Atingo de via kamerao malsukcesis", "cameraPermission": "Eraro akirante permeson por kamerao", @@ -124,42 +140,46 @@ "noPermission": "Permeso ne estis donita", "previewUnavailable": "Antaŭrigardo ne disponeblas", "selectADevice": "Elektu aparaton", - "testAudio": "" + "testAudio": "Ludi testan sonon" }, "dialog": { "accessibilityLabel": { - "liveStreaming": "Tuja Elsendfluo" + "liveStreaming": "Tuja elsendfluo" }, "allow": "Permesi", - "alreadySharedVideoMsg": "", + "alreadySharedVideoMsg": "Alia partoprenanto jam kundividas videon. Ĉi tiu kunveno permesas nur unu kundividata video samtempe.", "alreadySharedVideoTitle": "Nur unu video estas permesata samtempe.", "applicationWindow": "Programa fenestro", "Back": "Reen", - "cameraConstraintFailedError": "Via kamerao ne observas kelkajn neprajn limigojn.", + "cameraConstraintFailedError": "Via kamerao ne plenumas kelkajn neprajn postulojn.", "cameraNotFoundError": "Kamerao ne trovita.", "cameraNotSendingData": "Via kamerao ne atingeblas al ni. Bonvolu kontroli, ĉu alia programo jam uzas la aparaton, elekti alian de la agorda menuo, aŭ provu ĝisdatigi la programon.", "cameraNotSendingDataTitle": "Kamerao ne atingeblas", "cameraPermissionDeniedError": "Vi ne permesis uzi vian kameraon. Vi povas aliĝi al la kunveno, sed aliaj ne povos vin vidi. Vi povas tion ŝanĝi per la kameraa butono en la adresbreto.", "cameraUnknownError": "Ne eblas uzi la kameraon, pro kialo nekonata.", "cameraUnsupportedResolutionError": "Via kamerao ne subtenas la bezonatan distingivon.", - "Cancel": "Rezigni", + "Cancel": "Nuligi", "close": "Fermi", "conferenceDisconnectMsg": "Eble kontrolu vian retkonekton. Rekonekto post {{seconds}} sekundoj…", "conferenceDisconnectTitle": "Vi malkonektiĝis.", "conferenceReloadMsg": "Ni penas funkciigi ĉi tion. Rekonekto post {{seconds}} sekundoj…", - "conferenceReloadTitle": "Malfeliĉe, io misokazis.", - "confirm": "", - "confirmNo": "", + "conferenceReloadTitle": "Bedaŭrinde io fuŝiĝis.", + "confirm": "Konfirmi", + "confirmNo": "Ne", "confirmYes": "Jes", "connectError": "Oj! Io misokazis kaj ni ne povis vin konekti al la kunveno.", "connectErrorWithMsg": "Oj! Io misokazis kaj ni ne povis vin konekti al la kunveno: {{msg}}", - "connecting": "Konektanta", + "connecting": "Konektiĝo", "contactSupport": "Kontakti helpon", "copy": "Kopii", "dismiss": "Formeti", - "displayNameRequired": "", + "displayNameRequired": "Saluton! Kio estas via nomo?", "done": "Finita", - "enterDisplayName": "", + "e2eeDescription": "

    Tutvoja ĉifrado estas nuntempe EKSPERIMENTA. Bonvolu vidi ĉi tiun artikolon por detaloj.


    Konsciu, ke ŝalti tutvojan ĉifradon efektive malebligos servilflankajn servojn kiel ekzemple: registradon, tujan elsendfluon kaj telefonan partoprenon. Konsciu ankaŭ, ke la kunveno funkcios nur por homoj, kiuj uzas retumilon subtenantan enmetatajn fluojn.

    ", + "e2eeLabel": "Ŝlosilo", + "e2eeTitle": "Tutvoja ĉifrado", + "e2eeWarning": "

    ATENTIGO: Ne ĉiuj partoprenantoj en ĉi tiu kunveno ŝajnas havi subtenon de tutvoja ĉifrado. Se vi ŝaltos ĝin, ili ne povos vidi aŭ aŭdi vin.

    ", + "enterDisplayName": "Please enter your name here", "error": "Eraro", "externalInstallationMsg": "Vi devas instali nian ekranvidadan kromprogramon.", "externalInstallationTitle": "Kromprogramo bezonata", @@ -172,69 +192,80 @@ "inlineInstallExtension": "Instali nun", "internalError": "Oj! La jena eraro okazis: {{error}}", "internalErrorTitle": "Interna eraro", - "kickMessage": "", - "kickParticipantButton": "", - "kickParticipantDialog": "", - "kickParticipantTitle": "", - "kickTitle": "", - "liveStreaming": "Tuja Elsendfluo", - "liveStreamingDisabledForGuestTooltip": "", - "liveStreamingDisabledTooltip": "", + "kickMessage": "Vi povas kontakti {{participantDisplayName}} por pli da detaloj.", + "kickParticipantButton": "Forĵeti", + "kickParticipantDialog": "Ĉu vi certe volas forĵeti ĉi tiun partoprenanton?", + "kickParticipantTitle": "Forĵeti ĉi tiun partoprenanton?", + "kickTitle": "Aj! {{participantDisplayName}} forĵetis vin el la kunveno", + "liveStreaming": "Tuja elsendfluo", + "liveStreamingDisabledForGuestTooltip": "Gastoj ne povas komenci tujan elsendfluon.", + "liveStreamingDisabledTooltip": "Komenco de tuja elsendfluo malŝaltita.", "lockMessage": "Ŝloso de la kunveno malsukcesis.", - "lockRoom": "", + "lockRoom": "Aldoni $t(lockRoomPasswordUppercase) al la kunveno.", "lockTitle": "Ŝloso malsukcesis", "logoutQuestion": "Ĉu vi certe volas adiaŭi kaj fini la kunvenon?", - "logoutTitle": "Adiaŭi", - "maxUsersLimitReached": "", - "maxUsersLimitReachedTitle": "", + "logoutTitle": "Elsaluti", + "maxUsersLimitReached": "La maksimuma nombro de partoprenantoj atingita. La kunveno estas plena. Bonvolu kontakti la posedanton de la kunveno aŭ reprovi poste!", + "maxUsersLimitReachedTitle": "Maksimuma nombro de partoprenantoj atingita", "micConstraintFailedError": "Via mikrofono ne observas kelkajn neprajn limigojn.", "micNotFoundError": "Mikrofono ne trovita.", - "micNotSendingData": "", - "micNotSendingDataTitle": "", + "micNotSendingData": "Iru al la agordoj de via komputilo por malsilentigi vian mikrofonon kaj alĝustigi ĝian laŭtecon", + "micNotSendingDataTitle": "Via mikrofono estas silentigita en viaj sistemagordoj", "micPermissionDeniedError": "Vi ne permesis uzi vian mikrofonon. Vi povas aliĝi al la kunveno, sed aliaj ne povos vin aŭdi. Vi povas tion ŝanĝi per la kameraa butono en la adresbreto.", "micUnknownError": "Ne eblas uzi mikrofonon pro kialo nekonata.", - "muteParticipantBody": "Vi ne povos ĝin malsilentigi, sed ĝi povas sin malsilentigi kiam ajn.", + "muteEveryoneElseDialog": "Silentiginte lin aŭ ŝin, vi ne povos malsilentigi la uzanton, sed li aŭ ŝi mem povos malsilentigi sin iam ajn.", + "muteEveryoneElseTitle": "Silentigi ĉiujn krom {{whom}}?", + "muteEveryoneDialog": "Ĉu vi certe volas silentigi ĉiujn? Vi ne povos malsilentigi ilin, sed ili mem povos malsilentigi sin iam ajn.", + "muteEveryoneTitle": "Silentigi ĉiujn?", + "muteEveryoneSelf": "vin", + "muteEveryoneStartMuted": "Ĉiuj estas dekomence silentigitaj ekde nun", + "muteParticipantBody": "Vi ne povos lin aŭ ŝin malsilentigi, sed li aŭ ŝi povas malsilentigi sin iam ajn.", "muteParticipantButton": "Silentigi", - "muteParticipantDialog": "", + "muteParticipantDialog": "Ĉu vi certe volas silentigi ĉi tiun partoprenanton? Vi ne povos malsilentigi lin aŭ ŝin, sed li aŭ ŝi mem povos malsilentigi sin iam ajn.", "muteParticipantTitle": "Ĉu silentigi ĉi tiun partoprenanton?", "Ok": "Bone", - "passwordLabel": "", + "passwordLabel": "La kunvenon ŝlosis partoprenanto. Bonvolu entajpi $t(lockRoomPassword) por aliĝi.", "passwordNotSupported": "Agordo de kunvena pasvorto ne estas subtenata", - "passwordNotSupportedTitle": "", - "passwordRequired": "", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) ne subtenata", + "passwordRequired": "$t(lockRoomPasswordUppercase) deviga", "popupError": "Via foliumilo forbaras ŝprucfenestrojn de tiu ĉi retejo. Bonvolu permesi ŝprucfenestrojn en la prisekuraj agordoj de via fenestro kaj reprovi.", "popupErrorTitle": "Ŝprucfenestro barita", - "recording": "Registranta", - "recordingDisabledForGuestTooltip": "", - "recordingDisabledTooltip": "", + "recording": "Registrado", + "recordingDisabledForGuestTooltip": "Gastoj ne povas komenci registri.", + "recordingDisabledTooltip": "Komenco de registrado malŝaltita", "rejoinNow": "Realiĝi nun", "remoteControlAllowedMessage": "{{user}} akceptis vian teleregan peton!", "remoteControlDeniedMessage": "{{user}} rifuzis vian teleregan peton!", "remoteControlErrorMessage": "Eraro petante teleregajn permesojn de {{user}}!", "remoteControlRequestMessage": "Ĉu vi permesos al {{user}} teleregi vian komputilon?", - "remoteControlShareScreenWarning": "Sciu, ke se vi premos «Permesi», vi kunhavigos vian ekranon!", + "remoteControlShareScreenWarning": "Sciu, ke se vi premos “Permesi”, vi kunhavigos vian ekranon!", "remoteControlStopMessage": "Telerega seanco finita!", "remoteControlTitle": "Labortabla telerego", "Remove": "Forigi", - "removePassword": "", + "removePassword": "Forigi $t(lockRoomPassword)", "removeSharedVideoMsg": "Ĉu vi vere volas forigi vian kunhavatan videon?", "removeSharedVideoTitle": "Forigi kunhavatan videon", - "reservationError": "Rezerva sistema eraro", + "reservationError": "Rezervosistema eraro", "reservationErrorMsg": "Kodo de eraro: {{code}}, mesaĝo: {{msg}}", "retry": "Reprovi", - "screenSharingFailedToInstall": "Oj! Via ekranvidada kromprogramo malsukcesis instalon.", - "screenSharingFailedToInstallTitle": "Ekranvidada kromprogramo malsukcesis instalon", - "screenSharingFirefoxPermissionDeniedError": "", - "screenSharingFirefoxPermissionDeniedTitle": "", - "screenSharingPermissionDeniedError": "Oj! Io misokazis pri la permesoj al via ekranvidada kromprogramo. Bonvolu reviziti kaj reprovi.", + "screenSharingAudio": "Kundividi sonon", + "screenSharingFailedToInstall": "Oj! Via ekrandivida kromprogramo ne sukcesis instaliĝi.", + "screenSharingFailedToInstallTitle": "Ekrandivida kromprogramo ne sukcesis instaliĝi", + "screenSharingFirefoxPermissionDeniedError": "Io fuŝiĝis, kiam ni provis kundividi vian ekranon. Bonvolu certigi, ke vi donis al ni la rajton fari tion..", + "screenSharingFirefoxPermissionDeniedTitle": "Oj! Ni ne sukcesis komenci ekrandividadon!", + "screenSharingPermissionDeniedError": "Oj! Io misokazis pri la permesoj al via ekrandivida kromprogramo. Bonvolu reŝargi kaj reprovi.", + "sendPrivateMessage": "Vi antaŭ nelonge ricevis privatan mesaĝon. Ĉu vi celis respondi al ĝi private, aŭ ĉu vi volas sendi vian mesaĝon al la grupo?", + "sendPrivateMessageCancel": "Sendi al la grupo", + "sendPrivateMessageOk": "Sendi private", + "sendPrivateMessageTitle": "Sendi private?", "serviceUnavailable": "Servo ne disponeblas", "sessTerminated": "Voko finita", - "Share": "Kunhavi", + "Share": "Kundividi", "shareVideoLinkError": "Bonvolu doni ĝustan ligilon de YouTube", - "shareVideoTitle": "Kunhavi videon", - "shareYourScreen": "Kunhavigi vian ekranon", - "shareYourScreenDisabled": "", - "shareYourScreenDisabledForGuest": "", + "shareVideoTitle": "Kundividi videon", + "shareYourScreen": "Kundividi vian ekranon", + "shareYourScreenDisabled": "Kundividado de ekrano malŝaltita.", + "shareYourScreenDisabledForGuest": "Gastoj ne povas kundividi ekranon.", "startLiveStreaming": "Fini tujan elsendfluon", "startRecording": "Fini registradon", "startRemoteControlErrorMessage": "Eraro okazis dum komenco de la telerega seanco!", @@ -242,24 +273,30 @@ "stopRecording": "Fini registradon", "stopRecordingWarning": "Ĉu vi certe volas fini la registradon?", "stopStreamingWarning": "Ĉu vi certe volas fini la tujan elsendfluon?", - "streamKey": "", + "streamKey": "Ŝlosilo de tuja elsendfluo", "Submit": "Sendi", - "thankYou": "Dankon ĉar vi uzas {{appName}}!", + "thankYou": "Dankon ke vi uzas {{appName}}!", "token": "ĵetono", "tokenAuthFailed": "Pardonu, vi ne rajtas aliĝi al ĉi tiu voko.", "tokenAuthFailedTitle": "Aŭtentigo malsukcesis", - "transcribing": "", - "unlockRoom": "", + "transcribing": "transskribado", + "unlockRoom": "Forigi la $t(lockRoomPassword)n de la ĉambro", "userPassword": "uzantopasvorto", - "WaitForHostMsg": "", - "WaitForHostMsgWOk": "", - "WaitingForHost": "Atendanta la gastigan komputilon …", + "WaitForHostMsg": "La kunveno {{room}} ankoraŭ ne komencis. Se vi estas la gastiganto, bonvolu aŭtentiĝi. Alikaze atendu, ĝis la gastiganto venos.", + "WaitForHostMsgWOk": "La kunveno {{room}} ankoraŭ ne komencis. Se vi estas la gastiganto, bonvolu puŝi “Bone” por aŭtentiĝi. Alikaze atendu, ĝis la gastiganto venos.", + "WaitingForHost": "Atendo de la gastiga komputilo…", "Yes": "Jes", "yourEntireScreen": "Via tuta ekrano" }, "dialOut": { "statusMessage": "nun estas {{status}}" }, + "documentSharing": { + "title": "Kundividita dokumento" + }, + "e2ee": { + "labelToolTip": "Ĉiuj partoprenantoj en ĉi tiu kunveno ŝaltis tutvojan ĉifradon" + }, "feedback": { "average": "Mezbona", "bad": "Malbona", @@ -270,126 +307,131 @@ "veryGood": "Tre bona" }, "incomingCall": { - "answer": "", - "audioCallTitle": "", + "answer": "Respondi", + "audioCallTitle": "Alvenanta voko", "decline": "Formeti", - "productLabel": "", - "videoCallTitle": "" + "productLabel": "el Jitsi Meet", + "videoCallTitle": "Alvenanta vida voko" }, "info": { - "accessibilityLabel": "", - "addPassword": "", - "cancelPassword": "", - "conferenceURL": "", - "country": "", - "dialANumber": "", - "dialInConferenceID": "", - "dialInNotSupported": "", - "dialInNumber": "", - "dialInSummaryError": "", - "dialInTollFree": "", - "genericError": "", - "inviteLiveStream": "", - "invitePhone": "", - "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", - "liveStreamURL": "Tuja Elsendfluo", - "moreNumbers": "", - "noNumbers": "", + "accessibilityLabel": "Montri informojn", + "addPassword": "Aldoni $t(lockRoomPassword)n", + "cancelPassword": "Nuligi $t(lockRoomPassword)n", + "conferenceURL": "Ligilo:", + "country": "Lando", + "dialANumber": "Por aliĝi al via kunveno, voku unu el tiuj numeroj kaj poste entajpu la ciferkodon.", + "dialInConferenceID": "Ciferkodo:", + "dialInNotSupported": "Bedaŭrinde pertelefona vokaro ne estas nuntempe subtenata.", + "dialInNumber": "Voki la numeron :", + "dialInSummaryError": "Eraro dum venigo de telefonadaj informoj. Bonvolu reprovi poste.", + "dialInTollFree": "Senkosta numero", + "genericError": "Oj, io fuŝiĝis.", + "inviteLiveStream": "Por vidi la tujan elsendfluon, alklaku ĉi tiun ligilon: {{url}}", + "invitePhone": "Por aliĝi per telefono anstataŭe, tuŝu tion: {{number}},,{{conferenceID}}#\n", + "invitePhoneAlternatives": "Ĉu vi serĉas alian telefonnumeron?\nVidi la telefonnumerojn de la kunveno: {{url}}\n\n\nSe vi vokas ankaŭ per ĉambra telefono, vi povas aliĝi sen sono: {{silentUrl}}", + "inviteURLFirstPartGeneral": "Vi estas invitita al kunveno.", + "inviteURLFirstPartPersonal": "{{name}} invitas vin al kunveno.\n", + "inviteURLSecondPart": "\nAliĝi al la kunveno:\n{{url}}\n", + "liveStreamURL": "Tuja elsendfluo:", + "moreNumbers": "Pli da numeroj", + "noNumbers": "Neniu numero por voki.", "noPassword": "Neniu", - "noRoom": "", - "numbers": "", - "password": "", - "title": "Kunhavi", - "tooltip": "", - "label": "" + "noRoom": "Neniu ĉambro estis specifita por la voko.", + "numbers": "Telefonnumeroj", + "password": "$t(lockRoomPasswordUppercase):", + "title": "Kundividi", + "tooltip": "Kundividi ligilon kaj telefonnumeron por ĉi tiu kunveno", + "label": "Informoj pri la kunveno" }, "inviteDialog": { - "alertText": "", - "header": "", - "searchCallOnlyPlaceholder": "Enigu telefonnumeron", - "searchPeopleOnlyPlaceholder": "", - "searchPlaceholder": "", - "send": "" + "alertText": "La invito de kelkaj partoprenantoj malsukcesis.", + "header": "Inviti", + "searchCallOnlyPlaceholder": "Entajpu telefonnumeron", + "searchPeopleOnlyPlaceholder": "Serĉi partoprenantojn", + "searchPlaceholder": "Partoprenanto aŭ telefonnumero", + "send": "Sendi" }, "inlineDialogFailure": { - "msg": "Ni iom faletis.", - "retry": "Bonvolu reprovi", - "support": "Helpo", - "supportMsg": "Se tio ĉi ripetiĝos, kontaktiĝu kun" + "msg": "Ni iom stumblis.", + "retry": "Provi denove", + "support": "Subteno", + "supportMsg": "Se tio daŭre okazas, kontaktu" }, "keyboardShortcuts": { - "focusLocal": "Fokusi vian propran videon", - "focusRemote": "Fokusi videon de alia vokano", - "fullScreen": "Ŝalti / Malŝalti tutekranan reĝimon", + "focusLocal": "Fokusigi vian propran videon", + "focusRemote": "Fokusigi la videon de aliulo", + "fullScreen": "Ŝalti / malŝalti tutekranan reĝimon", "keyboardShortcuts": "Fulmoklavoj", - "localRecording": "", + "localRecording": "Montri aŭ kaŝi la ilojn por loka registrado", "mute": "Silentigi aŭ malsilentigi vian mikrofonon", "pushToTalk": "Premi por paroli", "raiseHand": "Levi aŭ mallevi manon", "showSpeakerStats": "Montri statistikon pri parolintoj", "toggleChat": "Malfermi aŭ fermi la babilon", - "toggleFilmstrip": "", + "toggleFilmstrip": "Montri aŭ kaŝi videajn bildetojn", "toggleScreensharing": "Komuti inter kameraa kaj ekrana vidado", - "toggleShortcuts": "", - "videoMute": "Ŝalti aŭ malŝalti vian kameraon" + "toggleShortcuts": "Montri aŭ kaŝi la fulmklavojn", + "videoMute": "Ŝalti aŭ malŝalti vian kameraon", + "videoQuality": "Agordi la kvaliton de vokado" }, "liveStreaming": { "busy": "Ni penas liberigi tujajn elsendilojn. Bonvolu reprovi post kelkaj minutoj.", - "busyTitle": "Ĉiuj elsendiloj nun okupiĝas", - "changeSignIn": "", - "choose": "", - "chooseCTA": "", - "enterStreamKey": "", + "busyTitle": "Ĉiuj elsendiloj estas nun okupataj", + "changeSignIn": "Ŝalti inter kontoj.", + "choose": "Elekti tujan elsendfluon", + "chooseCTA": "Elektu eblon por tuja elsendfluo. Vi nun estas ensalutinta kiel {{email}}.", + "enterStreamKey": "Enmetu ĉi tien vian ŝlosilon de tuja YouTube-elsendfluo.", "error": "Tuja elsendfluo malsukcesis. Bonvolu provi denove.", - "errorAPI": "", - "errorLiveStreamNotEnabled": "", - "expandedOff": "", - "expandedOn": "", - "expandedPending": "", + "errorAPI": "Eraro okazis dum alirado al viaj YouTube-dissendadoj. Bonvolu provi reensaluti.", + "errorLiveStreamNotEnabled": "Tuja elsendado ne estas ŝaltita ĉe {{email}}. Bonvolu ŝalti tujan elsendado aŭ ensaluti per konto kun ŝaltita tuja elsendado.", + "expandedOff": "La tuja elsendfluo ĉesis", + "expandedOn": "La kunveno estas nun elsendata al YouTube.", + "expandedPending": "La tuja elsendfluo nun startas…", "failedToStart": "Tuja elsendfluo malsukcesis komenci", - "getStreamKeyManually": "", - "invalidStreamKey": "", + "getStreamKeyManually": "Ni ne povis trovi tujajn elsendfluojn. Provu trovi vian ŝlosilon de tuja YouTube-elsendfluo.", + "invalidStreamKey": "La ŝlosilo de tuja elsendfluo povas esti malĝusta.", "off": "Tuja elsendfluo finiĝis", - "on": "Tuja Elsendfluo", - "pending": "Komencanta Tujan Elsendfluon…", - "serviceName": "", - "signedInAs": "", - "signIn": "", - "signInCTA": "", - "signOut": "", - "start": "Fini tujan elsendfluon", - "streamIdHelp": "", - "unavailableTitle": "Tuja elsendfluo ne disponeblas" + "offBy": "{{name}} ĉesigis la tujan elsendfluon", + "on": "Tuja elsendfluo", + "onBy": "{{name}} komencis la tujan elsendfluon", + "pending": "Startigo de tuja elsendfluo…", + "serviceName": "Servoj de tuja elsendado", + "signedInAs": "Vi nun estas ensalutinta kiel:", + "signIn": "Ensaluti kun Google", + "signInCTA": "Ensalutu aŭ entajpu vian ŝlosilon tuja elsendado el YouTube.", + "signOut": "Elsaluti", + "start": "Komenci tujan elsendfluon", + "streamIdHelp": "Kio estas tio?", + "unavailableTitle": "Tuja elsendfluo ne disponeblas", + "youtubeTerms": "Uzkondiĉoj de YouTube", + "googlePrivacyPolicy": "Privatecpolitiko de Google" }, "localRecording": { "clientState": { - "off": "", - "on": "", - "unknown": "" + "off": "Malŝaltita", + "on": "Ŝaltita", + "unknown": "Nekonata" }, - "dialogTitle": "", - "duration": "", - "durationNA": "", - "encoding": "", - "label": "", - "labelToolTip": "", - "localRecording": "", + "dialogTitle": "Iloj por loka registrado", + "duration": "Daŭro", + "durationNA": "neaplikebla", + "encoding": "Kodprezento", + "label": "REG", + "labelToolTip": "Loka registrado estas aktiva", + "localRecording": "Loka registrado", "me": "Mi", "messages": { - "engaged": "", - "finished": "", - "finishedModerator": "", - "notModerator": "" + "engaged": "Loka registrado estas aktiva.", + "finished": "Registrado de la sesio {{token}} estas finita. Bonvolu sendi la registritan dosieron al la kunvenestro.", + "finishedModerator": "Registrado de la sesio {{token}} estas finita. La registraĵo de la loka trako estis konservita. Bonvolu peti aliajn partoprenantojn, ke ili sendu sian registraĵon.", + "notModerator": "Vi ne estas la kunvenestro. Vi ne povas komenci aŭ fini lokan registradon." }, "moderator": "Kunvenestro", - "no": "", - "participant": "", - "participantStats": "", - "sessionToken": "", - "start": "Fini registradon", + "no": "Ne", + "participant": "Partoprenantoj", + "participantStats": "Statistikoj pri la partoprenantoj", + "sessionToken": "Sesia ĵetono", + "start": "Komenci registradon", "stop": "Fini registradon", "yes": "Jes" }, @@ -397,52 +439,56 @@ "lockRoomPasswordUppercase": "Pasvorto", "me": "mi", "notify": { - "connectedOneMember": "", - "connectedThreePlusMembers": "", - "connectedTwoMembers": "", + "connectedOneMember": "{{name}} aliĝis al la kunveno", + "connectedThreePlusMembers": "{{name}} kaj {{count}} aliaj aliĝis al la kunveno", + "connectedTwoMembers": "{{first}} kaj {{second}} aliĝis al la kunveno", "disconnected": "malkonektita", "focus": "Kunvena atento", - "focusFail": "{{component}} ne atingelbas - reprovo post {{ms}} sekundoj", + "focusFail": "{{component}} ne estas disponebla – reprovu post {{ms}} sekundoj", "grantedTo": "Kunvenestraj rajtoj donitaj al {{to}}!", - "invitedOneMember": "", - "invitedThreePlusMembers": "", - "invitedTwoMembers": "", - "kickParticipant": "", + "invitedOneMember": "{{name}} estis invitita", + "invitedThreePlusMembers": "{{name}} kaj {{count}} aliaj estis invititaj", + "invitedTwoMembers": "{{first}} kaj {{second}} estis invititaj", + "kickParticipant": "{{kicked}} estis forĵetita de {{kicker}}", "me": "Mi", "moderator": "Kunvenestraj rajtoj donitaj!", "muted": "Vi komencis la interparolon silente.", "mutedTitle": "Vi estas silentigita!", - "mutedRemotelyTitle": "", - "mutedRemotelyDescription": "", - "passwordRemovedRemotely": "", - "passwordSetRemotely": "", - "raisedHand": "", + "mutedRemotelyTitle": "Vi estis silentigita de {{participantDisplayName}}!", + "mutedRemotelyDescription": "Vi ĉiam povas malsilentiĝi, kiam vi estas preta paroli. Resilentiĝu, kiam vi finis, post eviti bruon en la kunveno.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) forigita de alia partoprenanto", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) difinita de alia partoprenanto", + "raisedHand": "{{name}} ŝatus paroli.", "somebody": "Iu", - "startSilentTitle": "", - "startSilentDescription": "", - "suboptimalExperienceDescription": "", - "suboptimalExperienceTitle": "", - "unmute": "", - "newDeviceCameraTitle": "", - "newDeviceAudioTitle": "", - "newDeviceAction": "" + "startSilentTitle": "Vi envenis sen sona eligo!", + "startSilentDescription": "Aliĝu denove al la kunveno por ŝalti sonon", + "suboptimalBrowserWarning": "N timas, ke vi ne havos bonegan sperton ĉi tie. Ni klopodas plibonigi tion, sed dume bonvolu provi uzi unu el la plene subtenataj retumiloj.", + "suboptimalExperienceTitle": "Atentigo pri la retumilo", + "unmute": "Malsilentigi", + "newDeviceCameraTitle": "Nova kamerao detektita", + "newDeviceAudioTitle": "Nova aparato detektita", + "newDeviceAction": "Uzi", + "OldElectronAPPTitle": "Sekurec-vundeblo!", + "oldElectronClientDescription1": "Ŝajnas, ke vi uzas malnovan version de la kliento de Jitsi Meet, kiu havas konatajn sekurec-vundeblojn. Bonvolu ĝisdatigi al nia ", + "oldElectronClientDescription2": "plej nova versio", + "oldElectronClientDescription3": " nun!" }, "passwordSetRemotely": "agordita de alia partoprenanto", - "passwordDigitsOnly": "", - "poweredby": "povigita de", + "passwordDigitsOnly": "Ĝis {{number}} ciferoj", + "poweredby": "pelata de", "presenceStatus": { - "busy": "", - "calling": "", + "busy": "Okupata", + "calling": "Vokado…", "connected": "Konektita", - "connecting": "Konektanta", - "connecting2": "Konektanta", + "connecting": "Konektiĝo", + "connecting2": "Konektiĝo*…", "disconnected": "Malkonektita", - "expired": "", - "ignored": "", - "initializingCall": "", - "invited": "", - "rejected": "", - "ringing": "" + "expired": "Eksvalidiĝinta", + "ignored": "Ignorata", + "initializingCall": "Startigo de la voko…", + "invited": "Invitita", + "rejected": "Malakceptita", + "ringing": "Sonorado…" }, "profile": { "setDisplayNameLabel": "Agordi vian videblan nomon", @@ -450,84 +496,93 @@ "setEmailLabel": "Retpoŝtadreso ligita al Gravatar", "title": "Profilo" }, + "raisedHand": "Ŝatus paroli", "recording": { - "authDropboxText": "", - "availableSpace": "", - "beta": "", + "authDropboxText": "Alŝuti al Dropbox", + "availableSpace": "Disponebla spaco: {{spaceLeft}} MB (proksimume {{duration}} minutoj da registraĵo)", + "beta": "BETA", "busy": "Ni penas liberigi registrilojn. Bonvolu reprovi post kelkaj minutoj.", - "busyTitle": "Ĉiuj registriloj nun okupiĝas", + "busyTitle": "Ĉiuj registriloj estas nun okupataj", "error": "Registrado malsukcesis. Bonvolu provi denove.", "expandedOff": "Registrado finita", - "expandedOn": "", - "expandedPending": "", + "expandedOn": "La kunveno estas nun registrata.", + "expandedPending": "Registrado komenciĝas", "failedToStart": "Registrado malsukcesis komenci", - "fileSharingdescription": "", - "live": "", - "loggedIn": "", + "fileSharingdescription": "Kundividi registraĵon kun aliaj partoprenantoj", + "live": "LIVE", + "loggedIn": "Logged in as {{userName}}", "off": "Registrado finita", + "offBy": "{{name}} ĉesigis la registradon", "on": "Registranta", - "pending": "", - "rec": "", - "serviceDescription": "", - "serviceName": "", - "signIn": "", - "signOut": "", - "unavailable": "", + "onBy": "{{name}} komencis la registradon", + "pending": "Prepariĝo por registrado de la kunveno…", + "rec": "REG", + "serviceDescription": "Via registraĵo estos konservita de la registra servo", + "serviceName": "Registra servo", + "signIn": "Ensaluti", + "signOut": "Elsaluti", + "unavailable": "Oj! La {{serviceName}} estas nun nedisponebla. Ni laboras por solvi la aferon. Bonvolu reprovi poste.", "unavailableTitle": "Registrado ne disponeblas" }, "sectionList": { - "pullToRefresh": "" + "pullToRefresh": "Tiru por aktualigi" }, "settings": { "calendar": { - "about": "", - "disconnect": "Malkonektita", - "microsoftSignIn": "", - "signedIn": "", - "title": "" + "about": "La integrigo de kalendaro {{appName}} estas uzata por sekure aliri vian kalendaron, por ke ĝi povu legi planitajn eventojn.", + "disconnect": "Malkonekti", + "microsoftSignIn": "Ensaluti per Microsoft", + "signedIn": "Alirado al kalendaraj eventoj por {{email}}. Klaku la buton “Malkonekti” sube por ĉesigi aliradon al kalendaraj eventoj.", + "title": "Kalendaro" }, - "devices": "", + "devices": "Aparatoj", "followMe": "Ĉiuj sekvas min", - "language": "", - "loggedIn": "", + "language": "Lingvo", + "loggedIn": "Ensalutinta kiels {{name}}", + "microphones": "Mikrofonoj", "moderator": "Kunvenestro", - "more": "", + "more": "Pli", "name": "Nomo", "noDevice": "Neniu", "selectAudioOutput": "Sona eligo", "selectCamera": "Kamerao", "selectMic": "Mikrofono", + "speakers": "Laŭparoliloj", "startAudioMuted": "Ĉiuj komenciĝas silentaj", "startVideoMuted": "Ĉiuj komenciĝas kaŝitaj", "title": "Agordoj" }, "settingsView": { - "alertOk": "", - "alertTitle": "Averto", - "alertURLText": "", - "buildInfoSection": "", - "conferenceSection": "", - "displayName": "", - "email": "", + "advanced": "Altnivela", + "alertOk": "Bone", + "alertTitle": "Atentigo", + "alertURLText": "La entajpita URL de servilo estas nevalida", + "buildInfoSection": "Informoj pri la versio", + "conferenceSection": "Konferenco", + "disableCallIntegration": "Malŝalti denaskan integrigon de vokoj", + "disableP2P": "Malŝalti la reĝimon de samtavola komunikado", + "displayName": "Uzantnomo", + "email": "Retadreso", "header": "Agordoj", "profileSection": "Profilo", - "serverURL": "", - "startWithAudioMuted": "", - "startWithVideoMuted": "", - "version": "" + "serverURL": "URL de servilo", + "showAdvanced": "Montri altnivelajn agordojn", + "startWithAudioMuted": "Komenci kun malŝaltita sono", + "startWithVideoMuted": "Komenci kun malŝaltita video", + "version": "Versio" }, "share": { - "dialInfoText": "", - "mainText": "" + "dialInfoText": "\n\n=====\n\nĈu vi volas simple voki per via telefono?\n\n{{defaultDialInNumber}}Alklaku ĉi tiun ligilon por vidi la telefonnumerojn por ĉi tiu kunveno\n{{dialInfoPageUrl}}", + "mainText": "Alklaku ĉi tiun ligilon por aliĝi al la kunveno:\n{{roomUrl}}" }, - "speaker": "Parolanto", + "speaker": "Laŭtparolilo", "speakerStats": { "hours": "{{count}}h", "minutes": "{{count}}m", "name": "Nomo", "seconds": "{{count}}s", - "speakerStats": "Statistikoj pri parolintoj", - "speakerTime": "Tempoj de parolintoj" + "speakerStats": "Statistikoj pri la parolanto", + "speakerTime": "Tempo de parolado" }, "startupoverlay": { "policyText": " ", @@ -540,94 +595,114 @@ }, "toolbar": { "accessibilityLabel": { - "audioOnly": "", - "audioRoute": "", - "callQuality": "", - "cc": "", - "chat": "", - "document": "Malfermi / Fermi komunan dokumenton", - "feedback": "", - "fullScreen": "", - "hangup": "", + "audioOnly": "Baskuligi nur-sonan reĝimon", + "audioRoute": "Elekti la sonaparaton", + "callQuality": "Agordi vidkvaliton", + "cc": "Baskuligi subtekstojn", + "chat": "Baskuligi tujmesaĝilan fenestron", + "document": "Baskuligi kundividitan dokumenton", + "download": "Elŝuti niajn aplikaĵojn", + "e2ee": "Tutvoja ĉifrado", + "feedback": "Lasi recenzon", + "fullScreen": "Baskuligi tutekranan reĝimon", + "hangup": "Forlasi la vokon", + "help": "Helpo", "invite": "Inviti homojn", - "kick": "", - "localRecording": "", - "lockRoom": "", - "moreActions": "", - "moreActionsMenu": "", - "mute": "", - "pip": "", + "kick": "Forĵeti partoprenanton", + "localRecording": "Baskuligi lokajn registrilojn", + "lockRoom": "Baskuligi pasvorton por la kunveno", + "moreActions": "Baskuligi la menuon kun pli da agoj", + "moreActionsMenu": "Menuo kun pli da agoj", + "moreOptions": "Montri pli da ebloj", + "mute": "Silentigi/malsilentigi", + "muteEveryone": "Silentigi ĉiujn", + "pip": "Baskuligi la reĝimon “bildo en bildo”", + "privateMessage": "Sendi privatan mesaĝon", "profile": "Redakti vian profilon", - "raiseHand": "", - "recording": "", - "remoteMute": "", - "Settings": "", - "sharedvideo": "", - "shareRoom": "", - "shareYourScreen": "", - "shortcuts": "", - "show": "", - "speakerStats": "", - "tileView": "", - "toggleCamera": "", - "videomute": "", - "videoblur": "" + "raiseHand": "Baskuligi manlevon", + "recording": "Baskuligi registradon", + "remoteMute": "Silentigi partoprenanton", + "Settings": "Baskuligi agordojn", + "sharedvideo": "Baskuligi kundividadon de videoj el YouTube", + "shareRoom": "Inviti iun", + "shareYourScreen": "Baskuligi kundividadon de ekrano", + "shortcuts": "Baskuligi fulmklavojn", + "show": "Montri sur scenejo", + "speakerStats": "Baskuligi statistikojn pri parolanto", + "tileView": "Baskuligi kahelan vidon", + "toggleCamera": "Baskuligi kameraon", + "toggleFilmstrip": "Baskuligi filmbendon", + "videomute": "Silentigi/malsilentigi videon", + "videoblur": "Baskuligi malnetigon de video" }, "addPeople": "Aldoni homojn al via voko", - "audioOnlyOff": "", - "audioOnlyOn": "", - "audioRoute": "", + "audioOnlyOff": "Malŝalti malalt-trafikan reĝimon", + "audioOnlyOn": "Ŝalti malalt-trafikan reĝimon", + "audioRoute": "Elekti la sonaparaton", "authenticate": "Aŭtentigi", - "callQuality": "", - "chat": "Malfermi / Fermi babilon", - "closeChat": "", - "documentClose": "Malfermi / Fermi komunan dokumenton", - "documentOpen": "Malfermi / Fermi komunan dokumenton", - "enterFullScreen": "", - "enterTileView": "", - "exitFullScreen": "", - "exitTileView": "", - "feedback": "", + "callQuality": "Agordi kvaliton de la voko", + "chat": "Malfermi / Fermi babilejon", + "closeChat": "Malfermi babilejon", + "documentClose": "Malfermi/Fermi komunan dokumenton", + "documentOpen": "Malfermi/Fermi komunan dokumenton", + "download": "Elŝuti niajn aplikaĵojn", + "e2ee": "Tutvoja ĉifrado", + "enterFullScreen": "Vidi tutekrane", + "enterTileView": "Vidi kahele", + "exitFullScreen": "Eliri el tutekrana reĝimo", + "exitTileView": "Eliri el kahela vido", + "feedback": "Lasi recenzon", "hangup": "Foriri", + "help": "Helpo", "invite": "Inviti homojn", - "login": "Saluti", - "logout": "Adiaŭi", - "lowerYourHand": "", - "moreActions": "", - "mute": "Siletnigi / Malsilentigi", - "openChat": "", - "pip": "", + "login": "Ensaluti", + "logout": "Elsaluti", + "lowerYourHand": "Malaltigi vian manon", + "moreActions": "Pli da agoj", + "moreOptions": "Pli da ebloj", + "mute": "Silentigi/Malsilentigi", + "muteEveryone": "Silentigi ĉiujn", + "noAudioSignalTitle": "Ne estas signalo el via mikrofono!", + "noAudioSignalDesc": "Se vi ne intence silentigis ĝin per viaj sistemaj agordoj aŭ fizike, konsideru transŝalti al alia aparato.", + "noAudioSignalDescSuggestion": "Se vi ne intence silentigis ĝin per viaj sistemaj agordoj aŭ fizike, konsideru transŝalti al la proponata aparato.", + "noAudioSignalDialInDesc": "Vi povas ankaŭ telefoni per:", + "noAudioSignalDialInLinkDesc": "Telefonnumeroj:", + "noisyAudioInputTitle": "Via mikrofono ŝajnas brua!", + "noisyAudioInputDesc": "Ŝajnas, ke via mikrofono faras bruon, bonvolu pripensi silentigi ĝin aŭ ŝanĝi aparaton.", + "openChat": "Malfermi la babilejon", + "pip": "Eniri reĝimon “bildo en bildo”", + "privateMessage": "Sendi privatan mesaĝon", "profile": "Redakti vian profilon", "raiseHand": "Levi / Mallevi vian manon", - "raiseYourHand": "", + "raiseYourHand": "Levi vian manon", "Settings": "Agordoj", - "sharedvideo": "Kunhavi videon de YouTube", - "shareRoom": "", - "shortcuts": "", + "sharedvideo": "Kundividi videon el YouTube", + "shareRoom": "Inviti iun", + "shortcuts": "Vidi fulmklavojn", "speakerStats": "Statistikoj pri parolintoj", - "startScreenSharing": "", - "startSubtitles": "", - "stopScreenSharing": "", - "stopSubtitles": "", - "stopSharedVideo": "", - "talkWhileMutedPopup": "Ĉu vi klopodas paroli? Vi estas silentigita.", - "tileViewToggle": "", - "toggleCamera": "", + "startScreenSharing": "Komenci dividadon de ekrano", + "startSubtitles": "Komenci subtekstojn", + "stopScreenSharing": "Ĉesigi dividadon de ekrano", + "stopSubtitles": "Ĉesigi subtekstojn", + "stopSharedVideo": "Haltigi videon el YouTube", + "talkWhileMutedPopup": "Ĉu vi provas paroli? Vi estas silentigita.", + "tileViewToggle": "Baskuligi titolan vidon", + "toggleCamera": "Baskuligi kameraon", "videomute": "Ŝalti / Malŝalti kameraon", - "startvideoblur": "", - "stopvideoblur": "" + "startvideoblur": "Malnetigi mian fonon", + "stopvideoblur": "Ĉesigi malnetigon de fono" }, "transcribing": { - "ccButtonTooltip": "", - "error": "Registrado malsukcesis. Bonvolu provi denove.", - "expandedLabel": "", - "failedToStart": "", - "labelToolTip": "", - "off": "", - "pending": "", - "start": "", - "stop": "", - "tr": "" + "ccButtonTooltip": "Komenci / Ĉesigi subtekstojn", + "error": "Transskribado malsukcesis. Bonvolu provi denove.", + "expandedLabel": "Transskribado estas ŝaltita", + "failedToStart": "Transskribado ne sukcesis komenciĝi", + "labelToolTip": "La kunveno estas transskribata", + "off": "Transskribado ĉesis", + "pending": "Prepariĝo por transskribado de la kunveno…", + "start": "Komenci montri subtekstojn", + "stop": "Ĉesi montri subtekstojn", + "tr": "TR" }, "userMedia": { "androidGrantPermissions": "Elektu Permesi kiam via foliumilo petos permesojn.", @@ -642,67 +717,84 @@ "safariGrantPermissions": "Elektu Bone kiam via foliumilo petos permesojn." }, "videoSIPGW": { - "busy": "", - "busyTitle": "", - "errorAlreadyInvited": "", - "errorInvite": "", - "errorInviteFailed": "", - "errorInviteFailedTitle": "", - "errorInviteTitle": "", - "pending": "" + "busy": "Ni klopodas liberigi rimedojn. Bonvolu reprovi post kelkaj minutoj.", + "busyTitle": "La ĉambra servo estas nun okupata", + "errorAlreadyInvited": "{{displayName}} jam estis invitita", + "errorInvite": "La kunveno ankoraŭ ne estas starigita. Bonvolu reprovi poste.", + "errorInviteFailed": "Ni klopodas solvi la problemon. Bonvolu reprovi poste.", + "errorInviteFailedTitle": "Invito de {{displayName}} malsukcesis", + "errorInviteTitle": "Eraro dum invito", + "pending": "{{displayName}} estis invitita" }, "videoStatus": { - "audioOnly": "", - "audioOnlyExpanded": "", - "callQuality": "", + "audioOnly": "SON", + "audioOnlyExpanded": "Vi estas en malalt-trafika reĝimo. En ĉi tiu reĝimo vi ricevos nur sonon kaj kundividatajn ekranojn.", + "callQuality": "Videa kvalito", "hd": "AD", + "hdTooltip": "La video estas en altkvalita distingivo", "highDefinition": "Altkvalita distingivo", - "labelTooiltipNoVideo": "", - "labelTooltipAudioOnly": "Nure sona reĝimo ŝaltita", + "labelTooiltipNoVideo": "Neniu video", + "labelTooltipAudioOnly": "Malalt-trafika reĝimo ŝaltita", "ld": "MD", - "lowDefinition": "Malaltkvalito distingivo", - "onlyAudioAvailable": "", - "onlyAudioSupported": "", + "ldTooltip": "La video estas en malaltkvalita distingivo", + "lowDefinition": "Malaltkvalita distingivo", + "onlyAudioAvailable": "Nur sono estas disponebla", + "onlyAudioSupported": "En ĉi tiu retumilo ni subtenas nur sonon.", + "p2pEnabled": "Samtavola reĝimo ŝaltita", + "p2pVideoQualityDescription": "En samtavola reĝimo, la kvalito de ricevata video povas esti ŝaltita nur inter “altkvalita” kaj “nur sono”. Aliaj agordoj estos aplikataj, dum la samtavola reĝimo estos akvita.", + "recHighDefinitionOnly": "Preferos altkvalitan distingivon.", "sd": "ND", + "sdTooltip": "La video estas en normalkvalita distingivo", "standardDefinition": "Normalkvalita distingivo" }, "videothumbnail": { "domute": "Silentigi", + "domuteOthers": "Silentigi ĉiujn aliajn", "flip": "Renversi", - "kick": "Forpeli", + "kick": "Forĵeti", "moderator": "Kunvenestro", "mute": "Partoprenanto silentigita", "muted": "Silentigita", - "remoteControl": "Defora rego", - "show": "", - "videomute": "" + "remoteControl": "Komenci/Fini deforan regadon", + "show": "Montri sur scenejo", + "videomute": "La partoprenanto malŝaltis la kameraon" }, "welcomepage": { "accessibilityLabel": { - "join": "", - "roomname": "Enigu nomon de ĉambro" + "join": "Tuŝu por aliĝi", + "roomname": "Entajpu nomon de ĉambro" }, - "appDescription": "", + "appDescription": "Ek, videobabilu kun la tuta teamo. Fakte, invitu ĉiujn, kiujn vi konas. {{app}} estas plene ĉifrita kaj 100%-e malfermfonta vidkonferenca programo, kiun vi povas uzi tuttage, ĉiutage, senpage, sen devi krei konton.", "audioVideoSwitch": { - "audio": "", - "video": "" + "audio": "Voĉo", + "video": "Video" }, - "calendar": "", - "connectCalendarButton": "", - "connectCalendarText": "", - "enterRoomTitle": "", - "go": "IRI", - "join": "ALIĜI", - "info": "", + "calendar": "Kalendaro", + "connectCalendarButton": "Konektu vian kalendaron", + "connectCalendarText": "Konektu vian kalendaron por vidi ĉiujn viajn kunvenojn en {{app}}. Plie, vi povas aldoni viajn {{provider}}-kunvenojn al via kalendaro kaj lanĉi ilin per unu klako.", + "enterRoomTitle": "Komencu novan kunvenon", + "getHelp": "Ricevi helpo", + "roomNameAllowedChars": "La nomo de la kunveno ne povas enhavi la jenajn signojn: ?, &, :, ', \", %, #.", + "go": "EK", + "goSmall": "EK", + "join": "KREI / ALIĜI", + "info": "Informoj", "privacy": "Privateco", - "recentList": "", - "recentListDelete": "", - "recentListEmpty": "", - "reducedUIText": "", - "roomname": "Enigu nomon de ĉambro", - "roomnameHint": "", - "sendFeedback": "Sendi rimarkojn", - "terms": "Kondiĉoj", - "title": "" + "recentList": "Lastaj", + "recentListDelete": "Forigi", + "recentListEmpty": "Via listo de lastaj kunvenoj estas malplena. Babilu kun via teamo kaj vi trovos ĉi tie ĉiujn viajn lastajn kunvenojn.", + "reducedUIText": "Bonvenon all {{app}}!", + "roomname": "Entajpu nomon de ĉambro", + "roomnameHint": "Entajpu la nomon aŭ URL de la ĉambro, al kiu vi volas aliĝi. Vi povas elpensi nomon, nur sciigu ĝin al la aliaj homoj, kiujn vi renkontos, por ke ili entajpu la saman nomon.", + "sendFeedback": "Sendi retrokuplon", + "terms": "Uzkondiĉoj", + "title": "Sekuraj, multfunkciaj kaj plene senpagaj video-konferencoj" + }, + "lonelyMeetingExperience": { + "button": "Inviti aliajn", + "youAreAlone": "Vi estas la sola en la kunveno" + }, + "helpView": { + "header": "Helpejo" } } From 1891ce0b24df191b90f196d1c8a01f55a7e4b284 Mon Sep 17 00:00:00 2001 From: vp8x8 <37841821+vp8x8@users.noreply.github.com> Date: Wed, 15 Jul 2020 11:43:00 +0300 Subject: [PATCH 064/167] fix(prejoin): Align avatar image on small screens (#7300) Fixes: #7296 --- css/_premeeting-screens.scss | 9 ++++++++- .../base/premeeting/components/web/Preview.js | 12 +++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/css/_premeeting-screens.scss b/css/_premeeting-screens.scss index 7f490b867..71b14ae3c 100644 --- a/css/_premeeting-screens.scss +++ b/css/_premeeting-screens.scss @@ -197,9 +197,16 @@ text-align: center; } + .preview-avatar-container { + width: 100%; + height: 80%; + display: flex; + align-items: center; + justify-content: center; + } + .avatar { background: #A4B8D1; - margin: 200px auto 0 auto; } video { diff --git a/react/features/base/premeeting/components/web/Preview.js b/react/features/base/premeeting/components/web/Preview.js index 43d1ad71c..7dae4675e 100644 --- a/react/features/base/premeeting/components/web/Preview.js +++ b/react/features/base/premeeting/components/web/Preview.js @@ -54,11 +54,13 @@ function Preview(props: Props) {
    - +
    + +
    ); } From 2c42dd07732cd1e76e2cd369bd37d548912aa7bc Mon Sep 17 00:00:00 2001 From: Ivy Date: Wed, 15 Jul 2020 17:44:04 +0900 Subject: [PATCH 065/167] lang: update Japanese --- lang/main-ja.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lang/main-ja.json b/lang/main-ja.json index 0ac7a58c4..5915a7c9f 100644 --- a/lang/main-ja.json +++ b/lang/main-ja.json @@ -290,9 +290,9 @@ "inviteLiveStream": "この会議のライブストリームを表示するには、このリンクをクリックしてください:{{url}}", "invitePhone": "", "invitePhoneAlternatives": "", - "inviteURLFirstPartGeneral": "", - "inviteURLFirstPartPersonal": "", - "inviteURLSecondPart": "", + "inviteURLFirstPartGeneral": "あなたはミーティングに招待されました。", + "inviteURLFirstPartPersonal": "{{name}} があなたをミーティングに招待しました。\n", + "inviteURLSecondPart": "\nミーティングにご参加ください:\n{{url}}\n", "liveStreamURL": "ライブストリーム:", "moreNumbers": "その他の番号", "noNumbers": "ダイヤルイン番号はありません。", From fc6bd3667c26ae2700d075211f3281aecd4e3866 Mon Sep 17 00:00:00 2001 From: Dan Dascalescu Date: Wed, 15 Jul 2020 20:44:40 +1200 Subject: [PATCH 066/167] config: fix typo --- config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.js b/config.js index 369b1ec4c..b8a10e893 100644 --- a/config.js +++ b/config.js @@ -513,7 +513,7 @@ var config = { /** External API url used to receive branding specific information. If there is no url set or there are missing fields, the defaults are applied. - None of the fieds are mandatory and the response must have the shape: + None of the fields are mandatory and the response must have the shape: { // The hex value for the colour used as background backgroundColor: '#fff', From 7f5751b9185b406a55e22b0df204620f56eae1b0 Mon Sep 17 00:00:00 2001 From: utkarshmarwaha <63839120+utkarshmarwaha@users.noreply.github.com> Date: Wed, 15 Jul 2020 14:18:56 +0530 Subject: [PATCH 067/167] rn,flags: add flag to show/hide video share button --- react/features/base/flags/constants.js | 6 ++++++ .../youtube-player/components/VideoShareButton.js | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/react/features/base/flags/constants.js b/react/features/base/flags/constants.js index 2d3e2dd58..a32ecf80d 100644 --- a/react/features/base/flags/constants.js +++ b/react/features/base/flags/constants.js @@ -112,6 +112,12 @@ export const TILE_VIEW_ENABLED = 'tile-view.enabled'; */ export const TOOLBOX_ALWAYS_VISIBLE = 'toolbox.alwaysVisible'; +/** + * Flag indicating if the video share button should be enabled + * Default: enabled (true). + */ +export const VIDEO_SHARE_BUTTON_ENABLED = 'video-share.enabled'; + /** * Flag indicating if the welcome page should be enabled. * Default: disabled (false). diff --git a/react/features/youtube-player/components/VideoShareButton.js b/react/features/youtube-player/components/VideoShareButton.js index a39f0ca5b..83907d1e3 100644 --- a/react/features/youtube-player/components/VideoShareButton.js +++ b/react/features/youtube-player/components/VideoShareButton.js @@ -2,6 +2,7 @@ import type { Dispatch } from 'redux'; +import { getFeatureFlag, VIDEO_SHARE_BUTTON_ENABLED } from '../../base/flags'; import { translate } from '../../base/i18n'; import { IconShareVideo } from '../../base/icons'; import { getLocalParticipant } from '../../base/participants'; @@ -90,21 +91,26 @@ class VideoShareButton extends AbstractButton { * Maps part of the Redux state to the props of this component. * * @param {Object} state - The Redux state. + * @param {Object} ownProps - The properties explicitly passed to the component instance. * @private * @returns {Props} */ -function _mapStateToProps(state): Object { +function _mapStateToProps(state, ownProps): Object { const { ownerId, status: sharedVideoStatus } = state['features/youtube-player']; const localParticipantId = getLocalParticipant(state).id; + const enabled = getFeatureFlag(state, VIDEO_SHARE_BUTTON_ENABLED, true); + const { visible = enabled } = ownProps; if (ownerId !== localParticipantId) { return { _isDisabled: isSharingStatus(sharedVideoStatus), - _sharingVideo: false }; + _sharingVideo: false, + visible }; } return { - _sharingVideo: isSharingStatus(sharedVideoStatus) + _sharingVideo: isSharingStatus(sharedVideoStatus), + visible }; } From c8444a9a0de60db4da4d1184fc61b1b6226803cc Mon Sep 17 00:00:00 2001 From: yuriikaidan <68324454+yuriikaidan@users.noreply.github.com> Date: Wed, 15 Jul 2020 11:05:50 +0200 Subject: [PATCH 068/167] lang: update Polish translation --- lang/main-pl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/main-pl.json b/lang/main-pl.json index 43f8f1df5..0994de687 100644 --- a/lang/main-pl.json +++ b/lang/main-pl.json @@ -639,7 +639,7 @@ "raiseHand": "Podnieś / Opuść rękę", "raiseYourHand": "Podnieś rękę", "Settings": "Ustawienia", - "sharedvideo": "Udostępnij wideo w Youtube", + "sharedvideo": "Udostępnij wideo z Youtube", "shareRoom": "Zaproś kogoś", "shortcuts": "Wyświetl skróty", "speakerStats": "Statystyki mówców", From 035f720a50700823927b71f8e1d3e272dbfa0174 Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Wed, 15 Jul 2020 11:06:14 +0300 Subject: [PATCH 069/167] feat(prejoin): Add 'skip prejoin' button --- css/_prejoin.scss | 8 +-- css/_premeeting-screens.scss | 63 +++++++++++++++++++ .../components/web/PreMeetingScreen.js | 6 ++ .../premeeting/components/web/ToggleButton.js | 52 +++++++++++++++ .../base/premeeting/components/web/index.js | 1 + react/features/prejoin/components/Prejoin.js | 51 ++++++++++----- react/features/prejoin/functions.js | 10 +++ 7 files changed, 168 insertions(+), 23 deletions(-) create mode 100644 react/features/base/premeeting/components/web/ToggleButton.js diff --git a/css/_prejoin.scss b/css/_prejoin.scss index 52a34be64..9317c3f89 100644 --- a/css/_prejoin.scss +++ b/css/_prejoin.scss @@ -36,13 +36,7 @@ } &-checkbox-container { - align-items: center; - color: #fff; - display: none; - font-size: 13px; - justify-content: center; - line-height: 20px; - margin-top: 16px; + margin-bottom: 14px; width: 100%; } } diff --git a/css/_premeeting-screens.scss b/css/_premeeting-screens.scss index 71b14ae3c..c892100d6 100644 --- a/css/_premeeting-screens.scss +++ b/css/_premeeting-screens.scss @@ -216,3 +216,66 @@ width: 100%; } } + +@mixin flex-centered() { + align-items: center; + display: flex; + justify-content: center; +} + +@mixin icon-container($bg, $fill) { + .toggle-button-icon-container { + background: $bg; + + svg { + fill: $fill + } + } +} + +.toggle-button { + border-radius: 3px; + cursor: pointer; + color: #fff; + font-size: 13px; + height: 40px; + margin: 0 auto; + width: 320px; + + @include flex-centered(); + + svg { + fill: transparent; + } + + &:hover { + background: #1C2025; + + @include icon-container(#A4B8D1, #1C2025); + } + + &-container { + position: relative; + + @include flex-centered(); + } + + &-icon-container { + border-radius: 50%; + left: -22px; + padding: 2px; + position: absolute; + } + + &--toggled { + background: #75757A; + + &:hover { + background: #75757A; + + @include icon-container(#A4B8D1, #75757A); + } + + @include icon-container(#A4B8D1, #75757A); + } +} diff --git a/react/features/base/premeeting/components/web/PreMeetingScreen.js b/react/features/base/premeeting/components/web/PreMeetingScreen.js index f25170723..da067d95e 100644 --- a/react/features/base/premeeting/components/web/PreMeetingScreen.js +++ b/react/features/base/premeeting/components/web/PreMeetingScreen.js @@ -39,6 +39,11 @@ type Props = { */ title: string, + /** + * The 'Skip prejoin' button to be rendered (if any). + */ + skipPrejoinButton?: React$Node, + /** * True if the preview overlay should be muted, false otherwise. */ @@ -97,6 +102,7 @@ export default class PreMeetingScreen extends PureComponent {
+ { this.props.skipPrejoinButton } { this.props.footer } diff --git a/react/features/base/premeeting/components/web/ToggleButton.js b/react/features/base/premeeting/components/web/ToggleButton.js new file mode 100644 index 000000000..c4fdbb437 --- /dev/null +++ b/react/features/base/premeeting/components/web/ToggleButton.js @@ -0,0 +1,52 @@ +// @flow + +import React from 'react'; + +import { Icon, IconCheck } from '../../../icons'; + +const mainClass = 'toggle-button'; + +type Props = { + + /** + * Text of the button. + */ + children: React$Node, + + /** + * If the button is toggled or not. + */ + isToggled?: boolean, + + /** + * OnClick button handler. + */ + onClick: Function +} + +/** + * Button used as a toggle. + * + * @returns {ReactElement} + */ +function ToggleButton({ children, isToggled, onClick }: Props) { + const className = isToggled ? `${mainClass} ${mainClass}--toggled` : mainClass; + + return ( +
+
+
+ +
+ {children} +
+
+ ); +} + +export default ToggleButton; diff --git a/react/features/base/premeeting/components/web/index.js b/react/features/base/premeeting/components/web/index.js index 669a795da..18b58934f 100644 --- a/react/features/base/premeeting/components/web/index.js +++ b/react/features/base/premeeting/components/web/index.js @@ -3,3 +3,4 @@ export { default as ActionButton } from './ActionButton'; export { default as InputField } from './InputField'; export { default as PreMeetingScreen } from './PreMeetingScreen'; +export { default as ToggleButton } from './ToggleButton'; diff --git a/react/features/prejoin/components/Prejoin.js b/react/features/prejoin/components/Prejoin.js index 8156ecbe9..2ef67644a 100644 --- a/react/features/prejoin/components/Prejoin.js +++ b/react/features/prejoin/components/Prejoin.js @@ -7,7 +7,7 @@ import { getRoomName } from '../../base/conference'; import { translate } from '../../base/i18n'; import { Icon, IconPhone, IconVolumeOff } from '../../base/icons'; import { isVideoMutedByUser } from '../../base/media'; -import { ActionButton, InputField, PreMeetingScreen } from '../../base/premeeting'; +import { ActionButton, InputField, PreMeetingScreen, ToggleButton } from '../../base/premeeting'; import { connect } from '../../base/redux'; import { getDisplayName, updateSettings } from '../../base/settings'; import { getLocalJitsiVideoTrack } from '../../base/tracks'; @@ -21,7 +21,8 @@ import { isDeviceStatusVisible, isDisplayNameRequired, isJoinByPhoneButtonVisible, - isJoinByPhoneDialogVisible + isJoinByPhoneDialogVisible, + isPrejoinSkipped } from '../functions'; import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog'; @@ -29,6 +30,11 @@ import DeviceStatus from './preview/DeviceStatus'; type Props = { + /** + * Flag signaling if the 'skip prejoin' button is toggled or not. + */ + buttonIsToggled: boolean, + /** * Flag signaling if the device status is visible or not. */ @@ -145,22 +151,22 @@ class Prejoin extends Component { this._closeDialog = this._closeDialog.bind(this); this._showDialog = this._showDialog.bind(this); - this._onCheckboxChange = this._onCheckboxChange.bind(this); + this._onToggleButtonClick = this._onToggleButtonClick.bind(this); this._onDropdownClose = this._onDropdownClose.bind(this); this._onOptionsClick = this._onOptionsClick.bind(this); this._setName = this._setName.bind(this); } - _onCheckboxChange: () => void; + _onToggleButtonClick: () => void; /** - * Handler for the checkbox. + * Handler for the toggle button. * * @param {Object} e - The synthetic event. * @returns {void} */ - _onCheckboxChange(e) { - this.props.setSkipPrejoin(e.target.checked); + _onToggleButtonClick() { + this.props.setSkipPrejoin(!this.props.buttonIsToggled); } _onDropdownClose: () => void; @@ -250,7 +256,7 @@ class Prejoin extends Component { videoTrack } = this.props; - const { _closeDialog, _onCheckboxChange, _onDropdownClose, _onOptionsClick, _setName, _showDialog } = this; + const { _closeDialog, _onDropdownClose, _onOptionsClick, _setName, _showDialog } = this; const { showJoinByPhoneButtons } = this.state; return ( @@ -259,6 +265,7 @@ class Prejoin extends Component { name = { name } showAvatar = { showAvatar } showConferenceInfo = { showJoinActions } + skipPrejoinButton = { this._renderSkipPrejoinButton() } title = { t('prejoin.joinMeeting') } videoMuted = { !showCameraPreview } videoTrack = { videoTrack }> @@ -306,14 +313,6 @@ class Prejoin extends Component { - -
- - {t('prejoin.doNotShow')} -
)} { showDialog && ( @@ -333,6 +332,25 @@ class Prejoin extends Component { _renderFooter() { return this.props.deviceStatusVisible && ; } + + /** + * Renders the 'skip prejoin' button. + * + * @returns {React$Element} + */ + _renderSkipPrejoinButton() { + const { buttonIsToggled, t } = this.props; + + return ( +
+ + {t('prejoin.doNotShow')} + +
+ ); + } } /** @@ -346,6 +364,7 @@ function mapStateToProps(state): Object { const joinButtonDisabled = isDisplayNameRequired(state) && !name; return { + buttonIsToggled: isPrejoinSkipped(state), joinButtonDisabled, name, deviceStatusVisible: isDeviceStatusVisible(state), diff --git a/react/features/prejoin/functions.js b/react/features/prejoin/functions.js index 708b1e1a4..85b6770c0 100644 --- a/react/features/prejoin/functions.js +++ b/react/features/prejoin/functions.js @@ -36,6 +36,16 @@ export function isDisplayNameRequired(state: Object): boolean { || state['features/base/config'].requireDisplayName; } +/** + * Selector for determining if the user has chosen to skip prejoin page. + * + * @param {Object} state - The state of the app. + * @returns {boolean} + */ +export function isPrejoinSkipped(state: Object) { + return state['features/prejoin'].userSelectedSkipPrejoin; +} + /** * Returns the text for the prejoin status bar. * From b85cd2348f0137bf4ff4a12231d9de5ce869245d Mon Sep 17 00:00:00 2001 From: Gabriel Imre Date: Wed, 15 Jul 2020 13:13:28 +0300 Subject: [PATCH 070/167] feat: add grant moderator functionality --- lang/main.json | 4 ++ react/features/base/icons/svg/crown.svg | 3 + react/features/base/icons/svg/index.js | 1 + .../features/base/participants/actionTypes.js | 10 +++ react/features/base/participants/actions.js | 17 +++++ react/features/base/participants/functions.js | 18 +++-- .../features/base/participants/middleware.js | 8 +++ .../testing/components/TestConnectionInfo.js | 11 ++- .../AbstractGrantModeratorButton.js | 70 +++++++++++++++++++ .../AbstractGrantModeratorDialog.js | 66 +++++++++++++++++ .../components/native/GrantModeratorButton.js | 9 +++ .../components/native/GrantModeratorDialog.js | 32 +++++++++ .../components/native/RemoteVideoMenu.js | 3 + .../components/native/index.js | 3 + .../components/web/GrantModeratorButton.js | 60 ++++++++++++++++ .../components/web/GrantModeratorDialog.js | 38 ++++++++++ .../web/RemoteVideoMenuTriggerButton.js | 7 ++ .../remote-video-menu/components/web/index.js | 4 ++ 18 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 react/features/base/icons/svg/crown.svg create mode 100644 react/features/remote-video-menu/components/AbstractGrantModeratorButton.js create mode 100644 react/features/remote-video-menu/components/AbstractGrantModeratorDialog.js create mode 100644 react/features/remote-video-menu/components/native/GrantModeratorButton.js create mode 100644 react/features/remote-video-menu/components/native/GrantModeratorDialog.js create mode 100644 react/features/remote-video-menu/components/web/GrantModeratorButton.js create mode 100644 react/features/remote-video-menu/components/web/GrantModeratorDialog.js diff --git a/lang/main.json b/lang/main.json index 7ba137976..4758bc7dd 100644 --- a/lang/main.json +++ b/lang/main.json @@ -203,6 +203,8 @@ "enterDisplayName": "Please enter your name here", "error": "Error", "gracefulShutdown": "Our service is currently down for maintenance. Please try again later.", + "grantModeratorDialog": "Are you sure you want to make this participant a moderator?", + "grantModeratorTitle": "Grant moderator", "IamHost": "I am the host", "incorrectRoomLockPassword": "Incorrect password", "incorrectPassword": "Incorrect username or password", @@ -669,6 +671,7 @@ "e2ee": "End-to-End Encryption", "feedback": "Leave feedback", "fullScreen": "Toggle full screen", + "grantModerator": "Grant Moderator", "hangup": "Leave the call", "help": "Help", "invite": "Invite people", @@ -817,6 +820,7 @@ "domute": "Mute", "domuteOthers": "Mute everyone else", "flip": "Flip", + "grantModerator": "Grant Moderator", "kick": "Kick out", "moderator": "Moderator", "mute": "Participant is muted", diff --git a/react/features/base/icons/svg/crown.svg b/react/features/base/icons/svg/crown.svg new file mode 100644 index 000000000..c35d74923 --- /dev/null +++ b/react/features/base/icons/svg/crown.svg @@ -0,0 +1,3 @@ + + + diff --git a/react/features/base/icons/svg/index.js b/react/features/base/icons/svg/index.js index 11ee2e1b8..b9f38b58f 100644 --- a/react/features/base/icons/svg/index.js +++ b/react/features/base/icons/svg/index.js @@ -23,6 +23,7 @@ export { default as IconClosedCaption } from './closed_caption.svg'; export { default as IconConnectionActive } from './gsm-bars.svg'; export { default as IconConnectionInactive } from './ninja.svg'; export { default as IconCopy } from './copy.svg'; +export { default as IconCrown } from './crown.svg'; export { default as IconDeviceBluetooth } from './bluetooth.svg'; export { default as IconDeviceEarpiece } from './phone-talk.svg'; export { default as IconDeviceHeadphone } from './headset.svg'; diff --git a/react/features/base/participants/actionTypes.js b/react/features/base/participants/actionTypes.js index 377a45b5e..3e3eb1b7c 100644 --- a/react/features/base/participants/actionTypes.js +++ b/react/features/base/participants/actionTypes.js @@ -12,6 +12,16 @@ */ export const DOMINANT_SPEAKER_CHANGED = 'DOMINANT_SPEAKER_CHANGED'; +/** + * Create an action for granting moderator to a participant. + * + * { + * type: GRANT_MODERATOR, + * id: string + * } + */ +export const GRANT_MODERATOR = 'GRANT_MODERATOR'; + /** * Create an action for removing a participant from the conference. * diff --git a/react/features/base/participants/actions.js b/react/features/base/participants/actions.js index efb1f8382..c5b2dd32c 100644 --- a/react/features/base/participants/actions.js +++ b/react/features/base/participants/actions.js @@ -5,6 +5,7 @@ import { DOMINANT_SPEAKER_CHANGED, HIDDEN_PARTICIPANT_JOINED, HIDDEN_PARTICIPANT_LEFT, + GRANT_MODERATOR, KICK_PARTICIPANT, MUTE_REMOTE_PARTICIPANT, PARTICIPANT_ID_CHANGED, @@ -47,6 +48,22 @@ export function dominantSpeakerChanged(id, conference) { }; } +/** + * Create an action for granting moderator to a participant. + * + * @param {string} id - Participant's ID. + * @returns {{ + * type: GRANT_MODERATOR, + * id: string + * }} + */ +export function grantModerator(id) { + return { + type: GRANT_MODERATOR, + id + }; +} + /** * Create an action for removing a participant from the conference. * diff --git a/react/features/base/participants/functions.js b/react/features/base/participants/functions.js index afb1aa78a..718a21fe7 100644 --- a/react/features/base/participants/functions.js +++ b/react/features/base/participants/functions.js @@ -259,6 +259,16 @@ export function getYoutubeParticipant(stateful: Object | Function) { return participants.filter(p => p.isFakeParticipant)[0]; } +/** + * Returns true if the participant is a moderator. + * + * @param {string} participant - Participant object. + * @returns {boolean} + */ +export function isParticipantModerator(participant: Object) { + return participant?.role === PARTICIPANT_ROLE.MODERATOR; +} + /** * Returns true if all of the meeting participants are moderators. * @@ -269,13 +279,7 @@ export function getYoutubeParticipant(stateful: Object | Function) { export function isEveryoneModerator(stateful: Object | Function) { const participants = _getAllParticipants(stateful); - for (const participant of participants) { - if (participant.role !== PARTICIPANT_ROLE.MODERATOR) { - return false; - } - } - - return true; + return participants.every(isParticipantModerator); } /** diff --git a/react/features/base/participants/middleware.js b/react/features/base/participants/middleware.js index e30062a73..01cac6f90 100644 --- a/react/features/base/participants/middleware.js +++ b/react/features/base/participants/middleware.js @@ -15,6 +15,7 @@ import { playSound, registerSound, unregisterSound } from '../sounds'; import { DOMINANT_SPEAKER_CHANGED, + GRANT_MODERATOR, KICK_PARTICIPANT, MUTE_REMOTE_PARTICIPANT, PARTICIPANT_DISPLAY_NAME_CHANGED, @@ -86,6 +87,13 @@ MiddlewareRegistry.register(store => next => action => { break; } + case GRANT_MODERATOR: { + const { conference } = store.getState()['features/base/conference']; + + conference.grantOwner(action.id); + break; + } + case KICK_PARTICIPANT: { const { conference } = store.getState()['features/base/conference']; diff --git a/react/features/base/testing/components/TestConnectionInfo.js b/react/features/base/testing/components/TestConnectionInfo.js index 6d1993114..c0e9952a1 100644 --- a/react/features/base/testing/components/TestConnectionInfo.js +++ b/react/features/base/testing/components/TestConnectionInfo.js @@ -36,6 +36,11 @@ type Props = { */ _localUserId: string, + /** + * The local participant's role. + */ + _localUserRole: string, + /** * Indicates whether or not the test mode is currently on. Otherwise the * TestConnectionInfo component will not render. @@ -179,6 +184,9 @@ class TestConnectionInfo extends Component { + @@ -208,7 +216,8 @@ function _mapStateToProps(state) { return { _conferenceConnectionState: state['features/testing'].connectionState, _conferenceJoinedState: conferenceJoined.toString(), - _localUserId: localParticipant && localParticipant.id, + _localUserId: localParticipant?.id, + _localUserRole: localParticipant?.role, _testMode: isTestModeEnabled(state) }; } diff --git a/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js b/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js new file mode 100644 index 000000000..426f71890 --- /dev/null +++ b/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js @@ -0,0 +1,70 @@ +// @flow + +import { openDialog } from '../../base/dialog'; +import { IconCrown } from '../../base/icons'; +import { + getParticipantById, + isLocalParticipantModerator, + isParticipantModerator +} from '../../base/participants'; +import { AbstractButton } from '../../base/toolbox'; +import type { AbstractButtonProps } from '../../base/toolbox'; + +import { GrantModeratorDialog } from '.'; + +export type Props = AbstractButtonProps & { + + /** + * The redux {@code dispatch} function. + */ + dispatch: Function, + + /** + * The ID of the participant for whom to grant moderator status. + */ + participantID: string, + + /** + * The function to be used to translate i18n labels. + */ + t: Function +}; + +/** + * An abstract remote video menu button which kicks the remote participant. + */ +export default class AbstractGrantModeratorButton extends AbstractButton { + accessibilityLabel = 'toolbar.accessibilityLabel.grantModerator'; + icon = IconCrown; + label = 'videothumbnail.grantModerator'; + + /** + * Handles clicking / pressing the button, and kicks the participant. + * + * @private + * @returns {void} + */ + _handleClick() { + const { dispatch, participantID } = this.props; + + dispatch(openDialog(GrantModeratorDialog, { participantID })); + } +} + +/** + * Function that maps parts of Redux state tree into component props. + * + * @param {Object} state - Redux state. + * @param {Object} ownProps - Properties of component. + * @private + * @returns {{ + * visible: boolean + * }} + */ +export function _mapStateToProps(state: Object, ownProps: Props) { + const { participantID } = ownProps; + + return { + visible: isLocalParticipantModerator(state) && !isParticipantModerator(getParticipantById(state, participantID)) + }; +} diff --git a/react/features/remote-video-menu/components/AbstractGrantModeratorDialog.js b/react/features/remote-video-menu/components/AbstractGrantModeratorDialog.js new file mode 100644 index 000000000..74c7064fd --- /dev/null +++ b/react/features/remote-video-menu/components/AbstractGrantModeratorDialog.js @@ -0,0 +1,66 @@ +// @flow + +import { Component } from 'react'; + +import { + createRemoteVideoMenuButtonEvent, + sendAnalytics +} from '../../analytics'; +import { grantModerator } from '../../base/participants'; + +type Props = { + + /** + * The Redux dispatch function. + */ + dispatch: Function, + + /** + * The ID of the remote participant to be granted moderator rights. + */ + participantID: string, + + /** + * Function to translate i18n labels. + */ + t: Function +}; + +/** + * Abstract dialog to confirm granting moderator to a participant. + */ +export default class AbstractGrantModeratorDialog + extends Component { + /** + * Initializes a new {@code AbstractGrantModeratorDialog} instance. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._onSubmit = this._onSubmit.bind(this); + } + + _onSubmit: () => boolean; + + /** + * Callback for the confirm button. + * + * @private + * @returns {boolean} - True (to note that the modal should be closed). + */ + _onSubmit() { + const { dispatch, participantID } = this.props; + + sendAnalytics(createRemoteVideoMenuButtonEvent( + 'grant.moderator.button', + { + 'participant_id': participantID + })); + + dispatch(grantModerator(participantID)); + + return true; + } +} diff --git a/react/features/remote-video-menu/components/native/GrantModeratorButton.js b/react/features/remote-video-menu/components/native/GrantModeratorButton.js new file mode 100644 index 000000000..0f02a15d6 --- /dev/null +++ b/react/features/remote-video-menu/components/native/GrantModeratorButton.js @@ -0,0 +1,9 @@ +// @flow + +import { translate } from '../../../base/i18n'; +import { connect } from '../../../base/redux'; +import AbstractGrantModeratorButton, { + _mapStateToProps +} from '../AbstractGrantModeratorButton'; + +export default translate(connect(_mapStateToProps)(AbstractGrantModeratorButton)); diff --git a/react/features/remote-video-menu/components/native/GrantModeratorDialog.js b/react/features/remote-video-menu/components/native/GrantModeratorDialog.js new file mode 100644 index 000000000..4e36bc63d --- /dev/null +++ b/react/features/remote-video-menu/components/native/GrantModeratorDialog.js @@ -0,0 +1,32 @@ +// @flow + +import React from 'react'; + +import { ConfirmDialog } from '../../../base/dialog'; +import { translate } from '../../../base/i18n'; +import { connect } from '../../../base/redux'; +import AbstractGrantModeratorDialog + from '../AbstractGrantModeratorDialog'; + +/** + * Dialog to confirm a remote participant kick action. + */ +class GrantModeratorDialog extends AbstractGrantModeratorDialog { + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement} + */ + render() { + return ( + + ); + } + + _onSubmit: () => boolean; +} + +export default translate(connect()(GrantModeratorDialog)); diff --git a/react/features/remote-video-menu/components/native/RemoteVideoMenu.js b/react/features/remote-video-menu/components/native/RemoteVideoMenu.js index 1fe8da96c..42602f868 100644 --- a/react/features/remote-video-menu/components/native/RemoteVideoMenu.js +++ b/react/features/remote-video-menu/components/native/RemoteVideoMenu.js @@ -12,6 +12,7 @@ import { StyleType } from '../../../base/styles'; import { PrivateMessageButton } from '../../../chat'; import { hideRemoteVideoMenu } from '../../actions'; +import GrantModeratorButton from './GrantModeratorButton'; import KickButton from './KickButton'; import MuteButton from './MuteButton'; import PinButton from './PinButton'; @@ -98,6 +99,8 @@ class RemoteVideoMenu extends Component { buttons.push(); } + buttons.push(); + if (!_disableKick) { buttons.push(); } diff --git a/react/features/remote-video-menu/components/native/index.js b/react/features/remote-video-menu/components/native/index.js index 6c509ecae..085d8d196 100644 --- a/react/features/remote-video-menu/components/native/index.js +++ b/react/features/remote-video-menu/components/native/index.js @@ -1,5 +1,8 @@ // @flow +export { + default as GrantModeratorDialog +} from './GrantModeratorDialog'; export { default as KickRemoteParticipantDialog } from './KickRemoteParticipantDialog'; diff --git a/react/features/remote-video-menu/components/web/GrantModeratorButton.js b/react/features/remote-video-menu/components/web/GrantModeratorButton.js new file mode 100644 index 000000000..37906203d --- /dev/null +++ b/react/features/remote-video-menu/components/web/GrantModeratorButton.js @@ -0,0 +1,60 @@ +/* @flow */ + +import React from 'react'; + +import { translate } from '../../../base/i18n'; +import { IconCrown } from '../../../base/icons'; +import { connect } from '../../../base/redux'; +import AbstractGrantModeratorButton, { + _mapStateToProps, + type Props +} from '../AbstractGrantModeratorButton'; + +import RemoteVideoMenuButton from './RemoteVideoMenuButton'; + +declare var interfaceConfig: Object; + +/** + * Implements a React {@link Component} which displays a button for granting + * moderator to a participant. + */ +class GrantModeratorButton extends AbstractGrantModeratorButton { + /** + * Instantiates a new {@code GrantModeratorButton}. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._handleClick = this._handleClick.bind(this); + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement} + */ + render() { + const { participantID, t, visible } = this.props; + + if (!visible) { + return null; + } + + return ( + + ); + } + + _handleClick: () => void +} + +export default translate(connect(_mapStateToProps)(GrantModeratorButton)); diff --git a/react/features/remote-video-menu/components/web/GrantModeratorDialog.js b/react/features/remote-video-menu/components/web/GrantModeratorDialog.js new file mode 100644 index 000000000..b99275ebe --- /dev/null +++ b/react/features/remote-video-menu/components/web/GrantModeratorDialog.js @@ -0,0 +1,38 @@ +// @flow + +import React from 'react'; + +import { Dialog } from '../../../base/dialog'; +import { translate } from '../../../base/i18n'; +import { connect } from '../../../base/redux'; +import AbstractGrantModeratorDialog + from '../AbstractGrantModeratorDialog'; + +/** + * Dialog to confirm a grant moderator action. + */ +class GrantModeratorDialog extends AbstractGrantModeratorDialog { + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement} + */ + render() { + return ( + +
+ { this.props.t('dialog.grantModeratorDialog') } +
+
+ ); + } + + _onSubmit: () => boolean; +} + +export default translate(connect()(GrantModeratorDialog)); diff --git a/react/features/remote-video-menu/components/web/RemoteVideoMenuTriggerButton.js b/react/features/remote-video-menu/components/web/RemoteVideoMenuTriggerButton.js index b1ed1947c..d569d058f 100644 --- a/react/features/remote-video-menu/components/web/RemoteVideoMenuTriggerButton.js +++ b/react/features/remote-video-menu/components/web/RemoteVideoMenuTriggerButton.js @@ -8,6 +8,7 @@ import { Popover } from '../../../base/popover'; import { connect } from '../../../base/redux'; import { + GrantModeratorButton, MuteButton, MuteEveryoneElseButton, KickButton, @@ -195,6 +196,12 @@ class RemoteVideoMenuTriggerButton extends Component { ); } + buttons.push( + + ); + if (!_disableKick) { buttons.push( Date: Wed, 15 Jul 2020 13:07:10 +0300 Subject: [PATCH 071/167] fix(prejoin): Show decoded version of meeting uri --- .../base/premeeting/components/web/CopyMeetingUrl.js | 4 ++-- react/features/base/util/uri.js | 10 ++++++++++ .../add-people-dialog/web/CopyMeetingLinkSection.js | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/react/features/base/premeeting/components/web/CopyMeetingUrl.js b/react/features/base/premeeting/components/web/CopyMeetingUrl.js index 7725ab426..ed956ea22 100644 --- a/react/features/base/premeeting/components/web/CopyMeetingUrl.js +++ b/react/features/base/premeeting/components/web/CopyMeetingUrl.js @@ -6,7 +6,7 @@ import { getCurrentConferenceUrl } from '../../../connection'; import { translate } from '../../../i18n'; import { Icon, IconCopy, IconCheck } from '../../../icons'; import { connect } from '../../../redux'; -import { copyText } from '../../../util'; +import { copyText, getDecodedURI } from '../../../util'; type Props = { @@ -156,7 +156,7 @@ class CopyMeetingUrl extends Component { className = { `url ${showLinkCopied ? 'done' : ''}` } onClick = { _copyUrl } >
- { !showCopyLink && !showLinkCopied && url } + { !showCopyLink && !showLinkCopied && getDecodedURI(url) } { showCopyLink && t('prejoin.copyAndShare') } { showLinkCopied && t('prejoin.linkCopied') }
diff --git a/react/features/base/util/uri.js b/react/features/base/util/uri.js index 4ecaa8589..7e099c2af 100644 --- a/react/features/base/util/uri.js +++ b/react/features/base/util/uri.js @@ -591,3 +591,13 @@ export function addHashParamsToURL(url: URL, hashParamsToAdd: Object = {}) { return url; } + +/** + * Returns the decoded URI. + * + * @param {string} uri - The URI to decode. + * @returns {string} + */ +export function getDecodedURI(uri: string) { + return decodeURI(uri.replace(/^https?:\/\//i, '')); +} diff --git a/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js b/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js index baacd2db7..6dc327d80 100644 --- a/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js +++ b/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js @@ -4,7 +4,7 @@ import React, { useState } from 'react'; import { translate } from '../../../../base/i18n'; import { Icon, IconCheck, IconCopy } from '../../../../base/icons'; -import { copyText } from '../../../../base/util'; +import { copyText, getDecodedURI } from '../../../../base/util'; type Props = { @@ -82,7 +82,7 @@ function CopyMeetingLinkSection({ t, url }: Props) { ); } - const displayUrl = decodeURI(url.replace(/^https?:\/\//i, '')); + const displayUrl = getDecodedURI(url); return ( <> From 36d95ed51f1ba2b3b58e0437f70948017f9b7a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 15 Jul 2020 14:20:22 +0200 Subject: [PATCH 072/167] rn,conference: show lonely experience only after joining Showing the modal earlier is weird because it will be closed as soon as we connect. Also, we don't know if we are going to be alone until we join. --- .../conference/components/native/LonelyMeetingExperience.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/react/features/conference/components/native/LonelyMeetingExperience.js b/react/features/conference/components/native/LonelyMeetingExperience.js index c7cf75848..5752115cd 100644 --- a/react/features/conference/components/native/LonelyMeetingExperience.js +++ b/react/features/conference/components/native/LonelyMeetingExperience.js @@ -126,11 +126,12 @@ class LonelyMeetingExperience extends PureComponent { */ function _mapStateToProps(state): $Shape { const { disableInviteFunctions } = state['features/base/config']; + const { conference } = state['features/base/conference']; const flag = getFeatureFlag(state, INVITE_ENABLED, true); return { _isInviteFunctionsDiabled: !flag || disableInviteFunctions, - _isLonelyMeeting: getParticipantCount(state) === 1, + _isLonelyMeeting: conference && getParticipantCount(state) === 1, _styles: ColorSchemeRegistry.get(state, 'Conference') }; } From 1790c71c800da3b684fe77404b4623ff81ea8fa3 Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Wed, 15 Jul 2020 15:18:03 +0300 Subject: [PATCH 073/167] fix(disconnect) Fix quick disconnect errors --- conference.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conference.js b/conference.js index 5fb3a93eb..486f6c77c 100644 --- a/conference.js +++ b/conference.js @@ -411,6 +411,10 @@ function disconnect() { return Promise.resolve(); }; + if (!connection) { + return onDisconnected(); + } + return connection.disconnect().then(onDisconnected, onDisconnected); } From 11fd5363ce4015e5e91ffdf1b3cf0feef353b224 Mon Sep 17 00:00:00 2001 From: George Politis Date: Wed, 15 Jul 2020 15:55:30 +0200 Subject: [PATCH 074/167] fix: Avoid overwriting the max resolution requested by the tile-view. (#7320) Part of [1] replaces a `setPreferredVideoQuality` call with a `setMaxReceiverVideoQuality` call. The change was part of a bigger changeset that adds logic that tries to adjust the max based on reduced ui turned on or off and allow to set prefered through the config. However, by calling `setMaxReceiverVideoQuality` instead of `setPreferredVideoQuality`, the new feature overrides the lower resolution requested by tile-view earlier in some occasions. This PR reverts back to using `setPreferredVideoQuality` instead of `setMaxReceiverVideoQuality` as this achieves the same result without overwriting the max set by the tile-view. NOTE that this is a quick-fix and all the handling related to setting the receive resolution will be reworked soon. [1]: https://github.com/jitsi/jitsi-meet/commit/7d513738d2179b68a8b21233d2bc9e93d3da05ab --- react/features/conference/middleware.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react/features/conference/middleware.js b/react/features/conference/middleware.js index 6348e8d32..cf8fcf42f 100644 --- a/react/features/conference/middleware.js +++ b/react/features/conference/middleware.js @@ -6,7 +6,7 @@ import { VIDEO_QUALITY_LEVELS, conferenceLeft, getCurrentConference, - setMaxReceiverVideoQuality + setPreferredVideoQuality } from '../base/conference'; import { hideDialog, isDialogOpen } from '../base/dialog'; import { setActiveModalId } from '../base/modal'; @@ -33,7 +33,7 @@ MiddlewareRegistry.register(store => next => action => { dispatch(setFilmstripEnabled(!reducedUI)); dispatch( - setMaxReceiverVideoQuality( + setPreferredVideoQuality( reducedUI ? VIDEO_QUALITY_LEVELS.LOW : VIDEO_QUALITY_LEVELS.HIGH)); From 29805edd02bde0f77677c34169c5834f29114ddd Mon Sep 17 00:00:00 2001 From: Andrei Gavrilescu <51706180+andrei-gavrilescu@users.noreply.github.com> Date: Wed, 15 Jul 2020 18:22:00 +0300 Subject: [PATCH 075/167] feat(rtcstats): Integrate rtcstats (#6945) * Integrate rtcstats * expcetion handling / clean up * order imports * config fix * remove mock amplitude handler * additional comments * lint fix * address code review * move rtcstats middleware * link to jitsi rtcstats package * address code review * address code review / add ws onclose handler * add display name / bump rtcstats version * resolve import error --- config.js | 9 ++ package-lock.json | 7 ++ package.json | 1 + react/features/analytics/AnalyticsEvents.js | 20 ++++ react/features/analytics/functions.js | 12 ++ .../analytics/handlers/AmplitudeHandler.js | 13 ++ react/features/app/middlewares.any.js | 1 + react/features/rtcstats/RTCStats.js | 111 ++++++++++++++++++ react/features/rtcstats/index.js | 1 + react/features/rtcstats/logger.js | 5 + react/features/rtcstats/middleware.js | 79 +++++++++++++ 11 files changed, 259 insertions(+) create mode 100644 react/features/rtcstats/RTCStats.js create mode 100644 react/features/rtcstats/index.js create mode 100644 react/features/rtcstats/logger.js create mode 100644 react/features/rtcstats/middleware.js diff --git a/config.js b/config.js index b8a10e893..34d2fb05d 100644 --- a/config.js +++ b/config.js @@ -406,6 +406,15 @@ var config = { // The Amplitude APP Key: // amplitudeAPPKey: '' + // Configuration for the rtcstats server: + // In order to enable rtcstats one needs to provide a endpoint url. + // rtcstatsEndpoint: wss://rtcstats-server-pilot.jitsi.net/, + + // The interval at which rtcstats will poll getStats, defaults to 1000ms. + // If the value is set to 0 getStats won't be polled and the rtcstats client + // will only send data related to RTCPeerConnection events. + // rtcstatsPolIInterval: 1000 + // Array of script URLs to load as lib-jitsi-meet "analytics handlers". // scriptURLs: [ // "libs/analytics-ga.min.js", // google-analytics diff --git a/package-lock.json b/package-lock.json index ead52310b..0cbacf9f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15003,6 +15003,13 @@ "sdp": "^2.6.0" } }, + "rtcstats": { + "version": "github:jitsi/rtcstats#02a1a089d9a97d1414d216ff7d9c432253e50190", + "from": "github:jitsi/rtcstats#v6.1.3", + "requires": { + "@jitsi/js-utils": "1.0.0" + } + }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", diff --git a/package.json b/package.json index 8dfbd9022..845e59c6f 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "redux": "4.0.4", "redux-thunk": "2.2.0", "rnnoise-wasm": "github:jitsi/rnnoise-wasm.git#566a16885897704d6e6d67a1d5ac5d39781db2af", + "rtcstats": "github:jitsi/rtcstats#v6.1.3", "styled-components": "3.4.9", "util": "0.12.1", "uuid": "3.1.0", diff --git a/react/features/analytics/AnalyticsEvents.js b/react/features/analytics/AnalyticsEvents.js index 49e8fa1da..e84cd2b7a 100644 --- a/react/features/analytics/AnalyticsEvents.js +++ b/react/features/analytics/AnalyticsEvents.js @@ -538,6 +538,26 @@ export function createRemoteVideoMenuButtonEvent(buttonName, attributes) { }; } +/** + * The rtcstats websocket onclose event. We send this to amplitude in order + * to detect trace ws prematurely closing. + * + * @param {Object} closeEvent - The event with which the websocket closed. + * @returns {Object} The event in a format suitable for sending via + * sendAnalytics. + */ +export function createRTCStatsTraceCloseEvent(closeEvent) { + const event = { + action: 'trace.onclose', + source: 'rtcstats' + }; + + event.code = closeEvent.code; + event.reason = closeEvent.reason; + + return event; +} + /** * Creates an event indicating that an action related to video blur * occurred (e.g. It was started or stopped). diff --git a/react/features/analytics/functions.js b/react/features/analytics/functions.js index ede59c555..5ef0dc793 100644 --- a/react/features/analytics/functions.js +++ b/react/features/analytics/functions.js @@ -30,6 +30,16 @@ export function sendAnalytics(event: Object) { } } +/** + * Return saved amplitude identity info such as session id, device id and user id. We assume these do not change for + * the duration of the conference. + * + * @returns {Object} + */ +export function getAmplitudeIdentity() { + return analytics.amplitudeIdentityProps; +} + /** * Resets the analytics adapter to its initial state - removes handlers, cache, * disabled state, etc. @@ -92,6 +102,8 @@ export function createHandlers({ getState }: { getState: Function }) { try { const amplitude = new AmplitudeHandler(handlerConstructorOptions); + analytics.amplitudeIdentityProps = amplitude.getIdentityProps(); + handlers.push(amplitude); // eslint-disable-next-line no-empty } catch (e) {} diff --git a/react/features/analytics/handlers/AmplitudeHandler.js b/react/features/analytics/handlers/AmplitudeHandler.js index 6f64dca65..f4ca5d208 100644 --- a/react/features/analytics/handlers/AmplitudeHandler.js +++ b/react/features/analytics/handlers/AmplitudeHandler.js @@ -65,4 +65,17 @@ export default class AmplitudeHandler extends AbstractHandler { this._extractName(event), event); } + + /** + * Return amplitude identity information. + * + * @returns {Object} + */ + getIdentityProps() { + return { + sessionId: amplitude.getInstance(this._amplitudeOptions).getSessionId(), + deviceId: amplitude.getInstance(this._amplitudeOptions).options.deviceId, + userId: amplitude.getInstance(this._amplitudeOptions).options.userId + }; + } } diff --git a/react/features/app/middlewares.any.js b/react/features/app/middlewares.any.js index ec46cb67a..20d05f1c0 100644 --- a/react/features/app/middlewares.any.js +++ b/react/features/app/middlewares.any.js @@ -37,6 +37,7 @@ import '../recent-list/middleware'; import '../recording/middleware'; import '../rejoin/middleware'; import '../room-lock/middleware'; +import '../rtcstats/middleware'; import '../subtitles/middleware'; import '../toolbox/middleware'; import '../transcribing/middleware'; diff --git a/react/features/rtcstats/RTCStats.js b/react/features/rtcstats/RTCStats.js new file mode 100644 index 000000000..14c915c00 --- /dev/null +++ b/react/features/rtcstats/RTCStats.js @@ -0,0 +1,111 @@ +import rtcstatsInit from 'rtcstats/rtcstats'; +import traceInit from 'rtcstats/trace-ws'; + +import { + createRTCStatsTraceCloseEvent, + sendAnalytics +} from '../analytics'; + +import logger from './logger'; + +/** + * Filter out RTCPeerConnection that are created by callstats.io. + * + * @param {*} config - Config object sent to the PC c'tor. + * @returns {boolean} + */ +function connectionFilter(config) { + if (config && config.iceServers[0] && config.iceServers[0].urls) { + for (const iceUrl of config.iceServers[0].urls) { + if (iceUrl.indexOf('taas.callstats.io') >= 0) { + return true; + } + } + } +} + +/** + * Class that controls the rtcstats flow, because it overwrites and proxies global function it should only be + * initialized once. + */ +class RTCStats { + /** + * Initialize the rtcstats components. First off we initialize the trace, which is a wrapped websocket + * that does the actual communication with the server. Secondly, the rtcstats component is initialized, + * it overwrites GUM and PeerConnection global functions and adds a proxy over them used to capture stats. + * Note, lib-jitsi-meet takes references to these methods before initializing so the init method needs to be + * loaded before it does. + * + * @param {Object} options -. + * @param {string} options.rtcstatsEndpoint - The Amplitude app key required. + * @param {number} options.rtcstatsPollInterval - The getstats poll interval in ms. + * @returns {void} + */ + init(options) { + this.handleTraceWSClose = this.handleTraceWSClose.bind(this); + this.trace = traceInit(options.rtcstatsEndpoint, this.handleTraceWSClose); + rtcstatsInit(this.trace, options.rtcstatsPollInterval, [ '' ], connectionFilter); + this.initialized = true; + } + + /** + * Check whether or not the RTCStats is initialized. + * + * @returns {boolean} + */ + isInitialized() { + return this.initialized; + } + + /** + * Send identity data to rtcstats server, this will be reflected in the identity section of the stats dump. + * It can be generally used to send additional metadata that might be relevant such as amplitude user data + * or deployment specific information. + * + * @param {Object} identityData - Metadata object to send as identity. + * @returns {void} + */ + sendIdentityData(identityData) { + this.trace && this.trace('identity', null, identityData); + } + + /** + * Connect to the rtcstats server instance. Stats (data obtained from getstats) won't be send until the + * connect successfully initializes, however calls to GUM are recorded in an internal buffer even if not + * connected and sent once it is established. + * + * @returns {void} + */ + connect() { + this.trace && this.trace.connect(); + } + + /** + * Self explanatory; closes the web socked connection. + * Note, at the point of writing this documentation there was no method to reset the function overwrites, + * thus even if the websocket is closed the global function proxies are still active but send no data, + * this shouldn't influence the normal flow of the application. + * + * @returns {void} + */ + close() { + this.trace && this.trace.close(); + } + + /** + * The way rtcstats is currently designed the ws wouldn't normally be closed by the application logic but rather + * by the page being closed/reloaded. Using this assumption any onclose event is most likely something abnormal + * that happened on the ws. We then track this in order to determine how many rtcstats connection were closed + * prematurely. + * + * @param {Object} closeEvent - Event sent by ws onclose. + * @returns {void} + */ + handleTraceWSClose(closeEvent) { + logger.info('RTCStats trace ws closed', closeEvent); + + sendAnalytics(createRTCStatsTraceCloseEvent(closeEvent)); + } +} + +export default new RTCStats(); diff --git a/react/features/rtcstats/index.js b/react/features/rtcstats/index.js new file mode 100644 index 000000000..d43689289 --- /dev/null +++ b/react/features/rtcstats/index.js @@ -0,0 +1 @@ +import './middleware'; diff --git a/react/features/rtcstats/logger.js b/react/features/rtcstats/logger.js new file mode 100644 index 000000000..6887eea32 --- /dev/null +++ b/react/features/rtcstats/logger.js @@ -0,0 +1,5 @@ +// @flow + +import { getLogger } from '../base/logging/functions'; + +export default getLogger('features/rtcstats'); diff --git a/react/features/rtcstats/middleware.js b/react/features/rtcstats/middleware.js new file mode 100644 index 000000000..4a0c5ddd4 --- /dev/null +++ b/react/features/rtcstats/middleware.js @@ -0,0 +1,79 @@ +// @flow + +import { getAmplitudeIdentity } from '../analytics'; +import { + CONFERENCE_JOINED +} from '../base/conference'; +import { LIB_WILL_INIT } from '../base/lib-jitsi-meet'; +import { getLocalParticipant } from '../base/participants'; +import { MiddlewareRegistry } from '../base/redux'; + +import RTCStats from './RTCStats'; +import logger from './logger'; + +/** + * Middleware which intercepts lib-jitsi-meet initialization and conference join in order init the + * rtcstats-client. + * + * @param {Store} store - The redux store. + * @returns {Function} + */ +MiddlewareRegistry.register(store => next => action => { + const state = store.getState(); + const config = state['features/base/config']; + const { analytics } = config; + + switch (action.type) { + case LIB_WILL_INIT: { + if (analytics.rtcstatsEndpoint) { + // RTCStats "proxies" WebRTC functions such as GUM and RTCPeerConnection by rewriting the global + // window functions. Because lib-jitsi-meet uses references to those functions that are taken on + // init, we need to add these proxies before it initializes, otherwise lib-jitsi-meet will use the + // original non proxy versions of these functions. + try { + // Default poll interval is 1000ms if not provided in the config. + const pollInterval = analytics.rtcstatsPollInterval || 1000; + + // Initialize but don't connect to the rtcstats server wss, as it will start sending data for all + // media calls made even before the conference started. + RTCStats.init({ + rtcstatsEndpoint: analytics.rtcstatsEndpoint, + rtcstatsPollInterval: pollInterval + }); + } catch (error) { + logger.error('Failed to initialize RTCStats: ', error); + } + } + break; + } + case CONFERENCE_JOINED: { + if (analytics.rtcstatsEndpoint && RTCStats.isInitialized()) { + // Once the conference started connect to the rtcstats server and send data. + try { + RTCStats.connect(); + + const localParticipant = getLocalParticipant(state); + + // The current implementation of rtcstats-server is configured to send data to amplitude, thus + // we add identity specific information so we can corelate on the amplitude side. If amplitude is + // not configured an empty object will be sent. + // The current configuration of the conference is also sent as metadata to rtcstats server. + // This is done in order to facilitate queries based on different conference configurations. + // e.g. Find all RTCPeerConnections that connect to a specific shard or were created in a + // conference with a specific version. + RTCStats.sendIdentityData({ + ...getAmplitudeIdentity(), + ...config, + displayName: localParticipant?.name + }); + } catch (error) { + // If the connection failed do not impact jitsi-meet just silently fail. + logger.error('RTCStats connect failed with: ', error); + } + } + break; + } + } + + return next(action); +}); From 0bec7c7ab75ae34c1a630752b22ff51d0aeed173 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 22:39:27 +0000 Subject: [PATCH 076/167] chore(deps): bump lodash from 4.17.13 to 4.17.19 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.13 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.13...4.17.19) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0cbacf9f8..fb19f32a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10793,9 +10793,9 @@ } }, "lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==" + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "lodash.clonedeep": { "version": "4.5.0", diff --git a/package.json b/package.json index 845e59c6f..f20bb8e78 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "jwt-decode": "2.2.0", "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", - "lodash": "4.17.13", + "lodash": "4.17.19", "moment": "2.19.4", "moment-duration-format": "2.2.2", "pixelmatch": "5.1.0", From 29366a0029c3da05207d21d99e1f987b7575930d Mon Sep 17 00:00:00 2001 From: Gabriel Imre Date: Wed, 15 Jul 2020 17:01:16 +0300 Subject: [PATCH 077/167] feat: add test hint for grant moderator availability --- .../base/testing/components/TestConnectionInfo.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/react/features/base/testing/components/TestConnectionInfo.js b/react/features/base/testing/components/TestConnectionInfo.js index c0e9952a1..4bdca030e 100644 --- a/react/features/base/testing/components/TestConnectionInfo.js +++ b/react/features/base/testing/components/TestConnectionInfo.js @@ -184,6 +184,9 @@ class TestConnectionInfo extends Component { + @@ -201,12 +204,7 @@ class TestConnectionInfo extends Component { * * @param {Object} state - The Redux state. * @private - * @returns {{ - * _conferenceConnectionState: string, - * _conferenceJoinedState: string, - * _localUserId: string, - * _testMode: boolean - * }} + * @returns {Props} */ function _mapStateToProps(state) { const conferenceJoined From ad948bdbe2450f4ec1088b5e13e0ae4f9548222e Mon Sep 17 00:00:00 2001 From: paweldomas Date: Wed, 15 Jul 2020 13:22:56 -0500 Subject: [PATCH 078/167] feat(StateListenerRegistry): add 'deepEquals' option Adds an extra 'options' argument to the register method which allows to use deep equality instead of a shallow one when comparing the current and the previous selections. --- .../base/redux/StateListenerRegistry.js | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/react/features/base/redux/StateListenerRegistry.js b/react/features/base/redux/StateListenerRegistry.js index cc68decbf..6ae1abf3c 100644 --- a/react/features/base/redux/StateListenerRegistry.js +++ b/react/features/base/redux/StateListenerRegistry.js @@ -2,6 +2,7 @@ import type { Store } from 'redux'; +import { equals } from './functions'; import logger from './logger'; /** @@ -37,6 +38,18 @@ type Listener */ type Selector = (state: Object, prevSelection: any) => any; +/** + * Options that can be passed to the register method. + */ +type RegistrationOptions = { + + /** + * @property {boolean} [deepEquals=false] - whether or not a deep equals check should be performed on the selection + * returned by {@link Selector}. + */ + deepEquals: ?boolean +} + /** * A type of a {@link Selector}-{@link Listener} association in which the * {@code Listener} listens to changes in the values derived from a redux @@ -50,6 +63,11 @@ type SelectorListener = { */ listener: Listener, + /** + * The {@link RegistrationOptions} passed during the registration to be applied on the listener. + */ + options: ?RegistrationOptions, + /** * The {@code Selector} which selects values whose changes are listened to * by {@link listener}. @@ -94,8 +112,10 @@ class StateListenerRegistry { = selectorListener.selector( store.getState(), prevSelection); + const useDeepEquals = selectorListener?.options?.deepEquals; - if (prevSelection !== selection) { + if ((useDeepEquals && !equals(prevSelection, selection)) + || (!useDeepEquals && prevSelection !== selection)) { prevSelections.set(selectorListener, selection); selectorListener.listener(selection, store, prevSelection); } @@ -117,12 +137,14 @@ class StateListenerRegistry { * @param {Function} listener - The listener to register with this * {@code StateListenerRegistry} so that it gets invoked when the value * returned by the specified {@code selector} changes. + * @param {RegistrationOptions} [options] - Any options to be applied to the registration. * @returns {void} */ - register(selector: Selector, listener: Listener) { + register(selector: Selector, listener: Listener, options: ?RegistrationOptions) { this._selectorListeners.add({ listener, - selector + selector, + options }); } From bf7aa399472ae1a8d1c3d4a37058a92113cf2475 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Wed, 15 Jul 2020 15:30:44 -0500 Subject: [PATCH 079/167] ref: one place for setting max recv frame height Moves the logic from all different places into single state listener to combine all inputs into a single output. --- react/features/conference/middleware.js | 10 +---- react/features/filmstrip/actionTypes.js | 14 +++++- react/features/filmstrip/actions.native.js | 26 ++++++++++- .../filmstrip/components/native/TileView.js | 15 ++++--- react/features/filmstrip/middleware.web.js | 4 -- react/features/video-layout/subscriber.js | 6 --- react/features/video-quality/middleware.js | 44 +++++++++++++++++-- 7 files changed, 88 insertions(+), 31 deletions(-) diff --git a/react/features/conference/middleware.js b/react/features/conference/middleware.js index cf8fcf42f..b7d131105 100644 --- a/react/features/conference/middleware.js +++ b/react/features/conference/middleware.js @@ -3,10 +3,8 @@ import { appNavigate } from '../app/actions'; import { CONFERENCE_JOINED, KICKED_OUT, - VIDEO_QUALITY_LEVELS, conferenceLeft, - getCurrentConference, - setPreferredVideoQuality + getCurrentConference } from '../base/conference'; import { hideDialog, isDialogOpen } from '../base/dialog'; import { setActiveModalId } from '../base/modal'; @@ -32,12 +30,6 @@ MiddlewareRegistry.register(store => next => action => { dispatch(setToolboxEnabled(!reducedUI)); dispatch(setFilmstripEnabled(!reducedUI)); - dispatch( - setPreferredVideoQuality( - reducedUI - ? VIDEO_QUALITY_LEVELS.LOW - : VIDEO_QUALITY_LEVELS.HIGH)); - break; } diff --git a/react/features/filmstrip/actionTypes.js b/react/features/filmstrip/actionTypes.js index 509ecc09e..4d0576e13 100644 --- a/react/features/filmstrip/actionTypes.js +++ b/react/features/filmstrip/actionTypes.js @@ -34,7 +34,19 @@ export const SET_FILMSTRIP_VISIBLE = 'SET_FILMSTRIP_VISIBLE'; * * { * type: SET_TILE_VIEW_DIMENSIONS, - * dimensions: Object + * dimensions: { + * gridDimensions: { + * columns: number, + * height: number, + * visibleRows: number, + * width: number + * }, + * thumbnailSize: { + * height: number, + * width: number + * }, + * filmstripWidth: number + * } * } */ export const SET_TILE_VIEW_DIMENSIONS = 'SET_TILE_VIEW_DIMENSIONS'; diff --git a/react/features/filmstrip/actions.native.js b/react/features/filmstrip/actions.native.js index 0276b2853..39721a1ed 100644 --- a/react/features/filmstrip/actions.native.js +++ b/react/features/filmstrip/actions.native.js @@ -3,7 +3,8 @@ import { SET_FILMSTRIP_ENABLED, SET_FILMSTRIP_HOVERED, - SET_FILMSTRIP_VISIBLE + SET_FILMSTRIP_VISIBLE, + SET_TILE_VIEW_DIMENSIONS } from './actionTypes'; /** @@ -53,3 +54,26 @@ export function setFilmstripVisible(visible: boolean) { visible }; } + +/** + * Sets the dimensions of the tile view grid. The action is only partially implemented on native as not all + * of the values are currently used. Check the description of {@link SET_TILE_VIEW_DIMENSIONS} for the full set + * of properties. + * + * @param {Object} dimensions - The tile view dimensions. + * @param {Object} thumbnailSize - The size of an individual video thumbnail. + * @param {number} thumbnailSize.height - The height of an individual video thumbnail. + * @param {number} thumbnailSize.width - The width of an individual video thumbnail. + * @returns {{ + * type: SET_TILE_VIEW_DIMENSIONS, + * dimensions: Object + * }} + */ +export function setTileViewDimensions({ thumbnailSize }: Object) { + return { + type: SET_TILE_VIEW_DIMENSIONS, + dimensions: { + thumbnailSize + } + }; +} diff --git a/react/features/filmstrip/components/native/TileView.js b/react/features/filmstrip/components/native/TileView.js index 91c23e0b1..f86279fca 100644 --- a/react/features/filmstrip/components/native/TileView.js +++ b/react/features/filmstrip/components/native/TileView.js @@ -8,12 +8,9 @@ import { } from 'react-native'; import type { Dispatch } from 'redux'; -import { - getNearestReceiverVideoQualityLevel, - setMaxReceiverVideoQuality -} from '../../../base/conference'; import { connect } from '../../../base/redux'; import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants'; +import { setTileViewDimensions } from '../../actions'; import Thumbnail from './Thumbnail'; import styles from './styles'; @@ -266,10 +263,14 @@ class TileView extends Component { * @returns {void} */ _updateReceiverQuality() { - const { height } = this._getTileDimensions(); - const qualityLevel = getNearestReceiverVideoQualityLevel(height); + const { height, width } = this._getTileDimensions(); - this.props.dispatch(setMaxReceiverVideoQuality(qualityLevel)); + this.props.dispatch(setTileViewDimensions({ + thumbnailSize: { + height, + width + } + })); } } diff --git a/react/features/filmstrip/middleware.web.js b/react/features/filmstrip/middleware.web.js index 6ec99b69c..8abd7a92c 100644 --- a/react/features/filmstrip/middleware.web.js +++ b/react/features/filmstrip/middleware.web.js @@ -1,7 +1,6 @@ // @flow import Filmstrip from '../../../modules/UI/videolayout/Filmstrip'; -import { getNearestReceiverVideoQualityLevel, setMaxReceiverVideoQuality } from '../base/conference'; import { MiddlewareRegistry } from '../base/redux'; import { CLIENT_RESIZED } from '../base/responsive-ui'; import { @@ -48,9 +47,6 @@ MiddlewareRegistry.register(store => next => action => { if (shouldDisplayTileView(state)) { const { width, height } = state['features/filmstrip'].tileViewDimensions.thumbnailSize; - const qualityLevel = getNearestReceiverVideoQualityLevel(height); - - store.dispatch(setMaxReceiverVideoQuality(qualityLevel)); // Once the thumbnails are reactified this should be moved there too. Filmstrip.resizeThumbnailsForTileView(width, height, true); diff --git a/react/features/video-layout/subscriber.js b/react/features/video-layout/subscriber.js index fb76d35ea..89a9f67e8 100644 --- a/react/features/video-layout/subscriber.js +++ b/react/features/video-layout/subscriber.js @@ -2,10 +2,6 @@ import debounce from 'lodash/debounce'; -import { - VIDEO_QUALITY_LEVELS, - setMaxReceiverVideoQuality -} from '../base/conference'; import { getPinnedParticipant, pinParticipant @@ -32,8 +28,6 @@ StateListenerRegistry.register( dispatch(selectParticipant()); if (!displayTileView) { - dispatch(setMaxReceiverVideoQuality(VIDEO_QUALITY_LEVELS.HIGH)); - if (_getAutoPinSetting()) { _updateAutoPinnedParticipant(store); } diff --git a/react/features/video-quality/middleware.js b/react/features/video-quality/middleware.js index 227178dc0..6ba7806f9 100644 --- a/react/features/video-quality/middleware.js +++ b/react/features/video-quality/middleware.js @@ -1,8 +1,14 @@ // @flow -import { CONFERENCE_JOINED } from '../base/conference/actionTypes'; -import { setPreferredVideoQuality } from '../base/conference/actions'; -import { MiddlewareRegistry } from '../base/redux'; +import { + CONFERENCE_JOINED, + VIDEO_QUALITY_LEVELS, + getNearestReceiverVideoQualityLevel, + setMaxReceiverVideoQuality, + setPreferredVideoQuality +} from '../base/conference'; +import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; +import { shouldDisplayTileView } from '../video-layout'; import logger from './logger'; @@ -31,3 +37,35 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { return result; }); + +/** + * Implements a state listener in order to calculate max receiver video quality. + */ +StateListenerRegistry.register( + /* selector */ state => { + const { reducedUI } = state['features/base/responsive-ui']; + const _shouldDisplayTileView = shouldDisplayTileView(state); + const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize; + + return { + displayTileView: _shouldDisplayTileView, + reducedUI, + thumbnailHeight: thumbnailSize?.height + }; + }, + /* listener */ ({ displayTileView, reducedUI, thumbnailHeight }, { dispatch, getState }) => { + const { maxReceiverVideoQuality } = getState()['features/base/conference']; + let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.HIGH; + + if (reducedUI) { + newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW; + } else if (displayTileView && !Number.isNaN(thumbnailHeight)) { + newMaxRecvVideoQuality = getNearestReceiverVideoQualityLevel(thumbnailHeight); + } + + if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) { + dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality)); + } + }, { + deepEquals: true + }); From 5f5468995f13d75dae1d90d7892df7c1a62b3b0b Mon Sep 17 00:00:00 2001 From: Mihai Uscat Date: Wed, 11 Mar 2020 11:45:42 +0200 Subject: [PATCH 080/167] feat(chat): Make chat push content to the side in large view --- css/_chat.scss | 7 +-- css/_toolbars.scss | 5 +++ css/_videolayout_default.scss | 7 +++ modules/UI/videolayout/LargeVideoManager.js | 14 +++++- react/features/chat/actions.js | 14 +++--- react/features/chat/components/web/Chat.js | 28 +++++------- react/features/chat/constants.js | 5 +++ .../conference/components/web/Conference.js | 4 -- .../conference/components/web/index.js | 2 + .../large-video/components/LargeVideo.web.js | 13 +++++- .../toolbox/components/web/Toolbox.js | 45 +++++++++++-------- react/features/toolbox/functions.web.js | 5 ++- 12 files changed, 94 insertions(+), 55 deletions(-) diff --git a/css/_chat.scss b/css/_chat.scss index 9b9444843..3c28d1758 100644 --- a/css/_chat.scss +++ b/css/_chat.scss @@ -4,16 +4,11 @@ color: #FFF; display: flex; flex-direction: column; - /** - * Make the sidebar flush with the top of the toolbar. Take the size of - * the toolbar and subtract from 100%. - */ - height: calc(100% - #{$newToolbarSizeWithPadding}); + height: 100%; left: -$sidebarWidth; overflow: hidden; position: absolute; top: 0; - transition: left 0.5s; width: $sidebarWidth; z-index: $sideToolbarContainerZ; diff --git a/css/_toolbars.scss b/css/_toolbars.scss index 9ec65f25b..928a68d28 100644 --- a/css/_toolbars.scss +++ b/css/_toolbars.scss @@ -42,6 +42,11 @@ display: none; } + &.shift-right { + margin-left: $sidebarWidth; + width: calc(100% - #{$sidebarWidth}); + } + .toolbox-background { background-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); transition: bottom .3s ease-in; diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss index 67078ea20..c9d538fcb 100644 --- a/css/_videolayout_default.scss +++ b/css/_videolayout_default.scss @@ -181,6 +181,13 @@ visibility: hidden; z-index: $zindex2; } + + &.shift-right { + &#largeVideoContainer { + margin-left: $sidebarWidth; + width: calc(100% - #{$sidebarWidth}); + } + } } #localVideoWrapper { diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js index ed4cfd7b9..da16533ea 100644 --- a/modules/UI/videolayout/LargeVideoManager.js +++ b/modules/UI/videolayout/LargeVideoManager.js @@ -12,6 +12,7 @@ import { JitsiParticipantConnectionStatus } from '../../../react/features/base/lib-jitsi-meet'; import { VIDEO_TYPE } from '../../../react/features/base/media'; +import { CHAT_SIZE } from '../../../react/features/chat'; import { updateKnownLargeVideoResolution } from '../../../react/features/large-video'; @@ -323,7 +324,18 @@ export default class LargeVideoManager { * Update container size. */ updateContainerSize() { - this.width = UIUtil.getAvailableVideoWidth(); + let widthToUse = UIUtil.getAvailableVideoWidth(); + const { isOpen } = APP.store.getState()['features/chat']; + + if (isOpen) { + /** + * If chat state is open, we re-compute the container width + * by subtracting the default width of the chat. + */ + widthToUse -= CHAT_SIZE; + } + + this.width = widthToUse; this.height = window.innerHeight; } diff --git a/react/features/chat/actions.js b/react/features/chat/actions.js index d519d9ac4..99935c1db 100644 --- a/react/features/chat/actions.js +++ b/react/features/chat/actions.js @@ -1,5 +1,7 @@ // @flow +import VideoLayout from '../../../modules/UI/videolayout/VideoLayout'; + import { ADD_MESSAGE, CLEAR_MESSAGES, @@ -86,14 +88,14 @@ export function setPrivateMessageRecipient(participant: Object) { } /** - * Toggles display of the chat side panel. + * Toggles display of the chat side panel while also taking window + * resize into account. * - * @returns {{ - * type: TOGGLE_CHAT - * }} + * @returns {Function} */ export function toggleChat() { - return { - type: TOGGLE_CHAT + return function(dispatch: (Object) => Object) { + dispatch({ type: TOGGLE_CHAT }); + VideoLayout.onResize(); }; } diff --git a/react/features/chat/components/web/Chat.js b/react/features/chat/components/web/Chat.js index f96dbcc4c..22e1e2465 100644 --- a/react/features/chat/components/web/Chat.js +++ b/react/features/chat/components/web/Chat.js @@ -1,7 +1,6 @@ // @flow import React from 'react'; -import Transition from 'react-transition-group/Transition'; import { translate } from '../../../base/i18n'; import { Icon, IconClose } from '../../../base/icons'; @@ -84,11 +83,9 @@ class Chat extends AbstractChat { */ render() { return ( - - { this._renderPanelContent } - + <> + { this._renderPanelContent() } + ); } @@ -145,30 +142,25 @@ class Chat extends AbstractChat { ); } - _renderPanelContent: (string) => React$Node | null; + _renderPanelContent: () => React$Node | null; /** - * Renders the contents of the chat panel, depending on the current - * animation state provided by {@code Transition}. + * Renders the contents of the chat panel. * - * @param {string} state - The current display transition state of the - * {@code Chat} component, as provided by {@code Transition}. * @private * @returns {ReactElement | null} */ - _renderPanelContent(state) { - this._isExited = state === 'exited'; - + _renderPanelContent() { const { _isOpen, _showNamePrompt } = this.props; - const ComponentToRender = !_isOpen && state === 'exited' - ? null - : ( + const ComponentToRender = _isOpen + ? ( <> { this._renderChatHeader() } { _showNamePrompt ? : this._renderChat() } - ); + ) + : null; let className = ''; if (_isOpen) { diff --git a/react/features/chat/constants.js b/react/features/chat/constants.js index b7681532a..5d872d410 100644 --- a/react/features/chat/constants.js +++ b/react/features/chat/constants.js @@ -2,6 +2,11 @@ export const CHAT_VIEW_MODAL_ID = 'chatView'; +/** + * The size of the chat. + */ +export const CHAT_SIZE = 375; + /** * The audio ID of the audio element for which the {@link playAudio} action is * triggered when new chat message is received. diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index 3b49cdfd4..c4a47e8ed 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -28,10 +28,8 @@ import { } from '../AbstractConference'; import type { AbstractProps } from '../AbstractConference'; -import InviteMore from './InviteMore'; import Labels from './Labels'; import { default as Notice } from './Notice'; -import { default as Subject } from './Subject'; declare var APP: Object; declare var config: Object; @@ -201,8 +199,6 @@ class Conference extends AbstractConference { onMouseMove = { this._onShowToolbar }> - -
diff --git a/react/features/conference/components/web/index.js b/react/features/conference/components/web/index.js index 9e4a6107c..360e48e5d 100644 --- a/react/features/conference/components/web/index.js +++ b/react/features/conference/components/web/index.js @@ -3,3 +3,5 @@ export { default as Conference } from './Conference'; export { default as renderConferenceTimer } from './ConferenceTimerDisplay'; export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel'; +export { default as InviteMore } from './InviteMore'; +export { default as Subject } from './Subject'; diff --git a/react/features/large-video/components/LargeVideo.web.js b/react/features/large-video/components/LargeVideo.web.js index 4db151b52..aed30eb54 100644 --- a/react/features/large-video/components/LargeVideo.web.js +++ b/react/features/large-video/components/LargeVideo.web.js @@ -4,6 +4,7 @@ import React, { Component } from 'react'; import { Watermarks } from '../../base/react'; import { connect } from '../../base/redux'; +import { InviteMore, Subject } from '../../conference'; import { fetchCustomBrandingData } from '../../dynamic-branding'; import { Captions } from '../../subtitles/'; @@ -26,6 +27,11 @@ type Props = { */ _fetchCustomBrandingData: Function, + /** + * Prop that indicates whether the chat is open. + */ + _isChatOpen: boolean, + /** * Used to determine the value of the autoplay attribute of the underlying * video element. @@ -57,12 +63,15 @@ class LargeVideo extends Component { */ render() { const style = this._getCustomSyles(); + const className = `videocontainer${this.props._isChatOpen ? ' shift-right' : ''}`; return (
+ +
@@ -133,10 +142,12 @@ class LargeVideo extends Component { function _mapStateToProps(state) { const testingConfig = state['features/base/config'].testing; const { backgroundColor, backgroundImageUrl } = state['features/dynamic-branding']; + const { isOpen: isChatOpen } = state['features/chat']; return { _customBackgroundColor: backgroundColor, _customBackgroundImageUrl: backgroundImageUrl, + _isChatOpen: isChatOpen, _noAutoPlayVideo: testingConfig?.noAutoPlayVideo }; } diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js index 08c3f1f5c..2a80fb2cf 100644 --- a/react/features/toolbox/components/web/Toolbox.js +++ b/react/features/toolbox/components/web/Toolbox.js @@ -32,7 +32,7 @@ import { connect, equals } from '../../../base/redux'; import { OverflowMenuItem } from '../../../base/toolbox'; import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks'; import { VideoBlurButton } from '../../../blur'; -import { ChatCounter, toggleChat } from '../../../chat'; +import { CHAT_SIZE, ChatCounter, toggleChat } from '../../../chat'; import { SharedDocumentButton } from '../../../etherpad'; import { openFeedbackDialog } from '../../../feedback'; import { beginAddPeople } from '../../../invite'; @@ -322,6 +322,10 @@ class Toolbox extends Component { this._onSetOverflowVisible(false); this.props.dispatch(setToolbarHovered(false)); } + + if (this.props._chatOpen !== prevProps._chatOpen) { + this._onResize(); + } } /** @@ -344,9 +348,9 @@ class Toolbox extends Component { * @returns {ReactElement} */ render() { - const { _visible, _visibleButtons } = this.props; + const { _chatOpen, _visible, _visibleButtons } = this.props; const rootClassNames = `new-toolbox ${_visible ? 'visible' : ''} ${ - _visibleButtons.size ? '' : 'no-buttons'}`; + _visibleButtons.size ? '' : 'no-buttons'} ${_chatOpen ? 'shift-right' : ''}`; return (
{ * @returns {void} */ _onResize() { - const width = window.innerWidth; + let widthToUse = window.innerWidth; - if (this.state.windowWidth !== width) { - this.setState({ windowWidth: width }); + // Take chat size into account when resizing toolbox. + if (this.props._chatOpen) { + widthToUse -= CHAT_SIZE; + } + + if (this.state.windowWidth !== widthToUse) { + this.setState({ windowWidth: widthToUse }); } } @@ -1174,6 +1183,9 @@ class Toolbox extends Component { / 2 // divide by the number of groups(left and right group) ); + if (this._shouldShowButton('chat')) { + buttonsLeft.push('chat'); + } if (this._shouldShowButton('desktop') && this._isDesktopSharingButtonVisible()) { buttonsLeft.push('desktop'); @@ -1181,9 +1193,6 @@ class Toolbox extends Component { if (this._shouldShowButton('raisehand')) { buttonsLeft.push('raisehand'); } - if (this._shouldShowButton('chat')) { - buttonsLeft.push('chat'); - } if (this._shouldShowButton('closedcaptions')) { buttonsLeft.push('closedcaptions'); } @@ -1239,15 +1248,6 @@ class Toolbox extends Component { return (
- { buttonsLeft.indexOf('desktop') !== -1 - && this._renderDesktopSharingButton() } - { buttonsLeft.indexOf('raisehand') !== -1 - && } { buttonsLeft.indexOf('chat') !== -1 &&
{ tooltip = { t('toolbar.chat') } />
} + { buttonsLeft.indexOf('desktop') !== -1 + && this._renderDesktopSharingButton() } + { buttonsLeft.indexOf('raisehand') !== -1 + && } { buttonsLeft.indexOf('closedcaptions') !== -1 && diff --git a/react/features/toolbox/functions.web.js b/react/features/toolbox/functions.web.js index 624f82ae3..3f3f87053 100644 --- a/react/features/toolbox/functions.web.js +++ b/react/features/toolbox/functions.web.js @@ -1,6 +1,7 @@ // @flow import { hasAvailableDevices } from '../base/devices'; +import { isMobileBrowser } from '../base/environment/utils'; declare var interfaceConfig: Object; @@ -43,8 +44,10 @@ export function isToolboxVisible(state: Object) { visible } = state['features/toolbox']; const { audioSettingsVisible, videoSettingsVisible } = state['features/settings']; + const { isOpen } = state['features/chat']; + const isMobileChatOpen = isMobileBrowser() && isOpen; - return Boolean(!iAmSipGateway && (timeoutID || visible || alwaysVisible + return Boolean(!isMobileChatOpen && !iAmSipGateway && (timeoutID || visible || alwaysVisible || audioSettingsVisible || videoSettingsVisible)); } From f9d545c531054715404ee7bb0b5d9c5c2e174ea3 Mon Sep 17 00:00:00 2001 From: Mihai Uscat Date: Mon, 9 Mar 2020 13:54:54 +0200 Subject: [PATCH 081/167] feat(chat): Make chat push content to the side in tile view --- css/_base.scss | 20 ++++++ css/filmstrip/_tile_view.scss | 11 +++- react/features/filmstrip/actions.web.js | 16 ++++- .../filmstrip/components/native/TileView.js | 2 +- .../filmstrip/components/web/Filmstrip.js | 6 +- react/features/filmstrip/middleware.web.js | 17 +++-- react/features/filmstrip/subscriber.web.js | 66 ++++++++++++++++--- 7 files changed, 117 insertions(+), 21 deletions(-) diff --git a/css/_base.scss b/css/_base.scss index a717cca1f..50d9169f2 100644 --- a/css/_base.scss +++ b/css/_base.scss @@ -33,6 +33,26 @@ body { } } +/** + * AtlasKit sets a default margin on the rendered modals, so + * when the shift-right class is set when the chat opens, we + * pad the modal container in order for the modals to be centered + * while also taking the chat size into consideration. +*/ +@media (min-width: 480px + $sidebarWidth) { + .shift-right [class^="Modal__FillScreen"] { + padding-left: $sidebarWidth; + } +} + +/** + * Similarly, we offset the notifications when the chat is open by + * padding the container. +*/ +.shift-right [class^="styledFlagGroup-"] { + padding-left: $sidebarWidth; +} + .jitsi-icon svg { fill: white; } diff --git a/css/filmstrip/_tile_view.scss b/css/filmstrip/_tile_view.scss index 0bdf505ee..619b3e232 100644 --- a/css/filmstrip/_tile_view.scss +++ b/css/filmstrip/_tile_view.scss @@ -46,7 +46,16 @@ position: fixed; top: 0; width: 100%; - z-index: $filmstripVideosZ + z-index: $filmstripVideosZ; + + &.shift-right { + margin-left: $sidebarWidth; + width: calc(100% - #{$sidebarWidth}); + + #filmstripRemoteVideos { + width: calc(100vw - #{$sidebarWidth}); + } + } } /** diff --git a/react/features/filmstrip/actions.web.js b/react/features/filmstrip/actions.web.js index 67df96d9e..53de94268 100644 --- a/react/features/filmstrip/actions.web.js +++ b/react/features/filmstrip/actions.web.js @@ -1,5 +1,7 @@ // @flow +import { CHAT_SIZE } from '../chat/constants'; + import { SET_HORIZONTAL_VIEW_DIMENSIONS, SET_TILE_VIEW_DIMENSIONS } from './actionTypes'; import { calculateThumbnailSizeForHorizontalView, calculateThumbnailSizeForTileView } from './functions'; @@ -13,15 +15,25 @@ const TILE_VIEW_SIDE_MARGINS = 10 * 2; * * @param {Object} dimensions - Whether the filmstrip is visible. * @param {Object} windowSize - The size of the window. + * @param {boolean} isChatOpen - Whether the chat panel is displayed, in + * order to properly compute the tile view size. * @returns {{ * type: SET_TILE_VIEW_DIMENSIONS, * dimensions: Object * }} */ -export function setTileViewDimensions(dimensions: Object, windowSize: Object) { +export function setTileViewDimensions(dimensions: Object, windowSize: Object, isChatOpen: boolean) { + const { clientWidth, clientHeight } = windowSize; + let widthToUse = clientWidth; + + if (isChatOpen) { + widthToUse -= CHAT_SIZE; + } + const thumbnailSize = calculateThumbnailSizeForTileView({ ...dimensions, - ...windowSize + clientWidth: widthToUse, + clientHeight }); const filmstripWidth = dimensions.columns * (TILE_VIEW_SIDE_MARGINS + thumbnailSize.width); diff --git a/react/features/filmstrip/components/native/TileView.js b/react/features/filmstrip/components/native/TileView.js index f86279fca..62a03ac83 100644 --- a/react/features/filmstrip/components/native/TileView.js +++ b/react/features/filmstrip/components/native/TileView.js @@ -10,7 +10,7 @@ import type { Dispatch } from 'redux'; import { connect } from '../../../base/redux'; import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants'; -import { setTileViewDimensions } from '../../actions'; +import { setTileViewDimensions } from '../../actions.native'; import Thumbnail from './Thumbnail'; import styles from './styles'; diff --git a/react/features/filmstrip/components/web/Filmstrip.js b/react/features/filmstrip/components/web/Filmstrip.js index 05a008770..7e5a599f3 100644 --- a/react/features/filmstrip/components/web/Filmstrip.js +++ b/react/features/filmstrip/components/web/Filmstrip.js @@ -371,13 +371,15 @@ function _mapStateToProps(state) { const reduceHeight = !isFilmstripOnly && state['features/toolbox'].visible && interfaceConfig.TOOLBAR_BUTTONS.length; const remoteVideosVisible = shouldRemoteVideosBeVisible(state); - const className = `${remoteVideosVisible ? '' : 'hide-videos'} ${reduceHeight ? 'reduce-height' : ''}`.trim(); + const { isOpen: shiftRight } = state['features/chat']; + const className = `${remoteVideosVisible ? '' : 'hide-videos'} ${ + reduceHeight ? 'reduce-height' : '' + } ${shiftRight ? 'shift-right' : ''}`.trim(); const videosClassName = `filmstrip__videos${ isFilmstripOnly ? ' filmstrip__videos-filmstripOnly' : ''}${ visible ? '' : ' hidden'}`; const { gridDimensions = {}, filmstripWidth } = state['features/filmstrip'].tileViewDimensions; - return { _className: className, _columns: gridDimensions.columns, diff --git a/react/features/filmstrip/middleware.web.js b/react/features/filmstrip/middleware.web.js index 8abd7a92c..1c8525e1d 100644 --- a/react/features/filmstrip/middleware.web.js +++ b/react/features/filmstrip/middleware.web.js @@ -10,7 +10,7 @@ import { } from '../video-layout'; import { SET_HORIZONTAL_VIEW_DIMENSIONS, SET_TILE_VIEW_DIMENSIONS } from './actionTypes'; -import { setHorizontalViewDimensions, setTileViewDimensions } from './actions'; +import { setHorizontalViewDimensions, setTileViewDimensions } from './actions.web'; import './subscriber.web'; @@ -29,11 +29,18 @@ MiddlewareRegistry.register(store => next => action => { case LAYOUTS.TILE_VIEW: { const { gridDimensions } = state['features/filmstrip'].tileViewDimensions; const { clientHeight, clientWidth } = state['features/base/responsive-ui']; + const { isOpen } = state['features/chat']; - store.dispatch(setTileViewDimensions(gridDimensions, { - clientHeight, - clientWidth - })); + store.dispatch( + setTileViewDimensions( + gridDimensions, + { + clientHeight, + clientWidth + }, + isOpen + ) + ); break; } case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: diff --git a/react/features/filmstrip/subscriber.web.js b/react/features/filmstrip/subscriber.web.js index 2f01dfe0b..913a80b03 100644 --- a/react/features/filmstrip/subscriber.web.js +++ b/react/features/filmstrip/subscriber.web.js @@ -5,7 +5,7 @@ import VideoLayout from '../../../modules/UI/videolayout/VideoLayout'; import { StateListenerRegistry, equals } from '../base/redux'; import { getCurrentLayout, getTileViewGridDimensions, shouldDisplayTileView, LAYOUTS } from '../video-layout'; -import { setHorizontalViewDimensions, setTileViewDimensions } from './actions'; +import { setHorizontalViewDimensions, setTileViewDimensions } from './actions.web'; /** * Listens for changes in the number of participants to calculate the dimensions of the tile view grid and the tiles. @@ -19,12 +19,19 @@ StateListenerRegistry.register( const gridDimensions = getTileViewGridDimensions(state); const oldGridDimensions = state['features/filmstrip'].tileViewDimensions.gridDimensions; const { clientHeight, clientWidth } = state['features/base/responsive-ui']; + const { isOpen } = state['features/chat']; if (!equals(gridDimensions, oldGridDimensions)) { - store.dispatch(setTileViewDimensions(gridDimensions, { - clientHeight, - clientWidth - })); + store.dispatch( + setTileViewDimensions( + gridDimensions, + { + clientHeight, + clientWidth + }, + isOpen + ) + ); } } }); @@ -40,12 +47,18 @@ StateListenerRegistry.register( switch (layout) { case LAYOUTS.TILE_VIEW: { const { clientHeight, clientWidth } = state['features/base/responsive-ui']; + const { isOpen } = state['features/chat']; - store.dispatch(setTileViewDimensions( - getTileViewGridDimensions(state), { - clientHeight, - clientWidth - })); + store.dispatch( + setTileViewDimensions( + getTileViewGridDimensions(state), + { + clientHeight, + clientWidth + }, + isOpen + ) + ); break; } case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: @@ -76,3 +89,36 @@ StateListenerRegistry.register( } } ); + +/** + * Listens for changes in the chat state to calculate the dimensions of the tile view grid and the tiles. + */ +StateListenerRegistry.register( + /* selector */ state => state['features/chat'].isOpen, + /* listener */ (isChatOpen, store) => { + const state = store.getState(); + + if (isChatOpen) { + // $FlowFixMe + document.body.classList.add('shift-right'); + } else { + // $FlowFixMe + document.body.classList.remove('shift-right'); + } + + if (shouldDisplayTileView(state)) { + const gridDimensions = getTileViewGridDimensions(state); + const { clientHeight, clientWidth } = state['features/base/responsive-ui']; + + store.dispatch( + setTileViewDimensions( + gridDimensions, + { + clientHeight, + clientWidth + }, + isChatOpen + ) + ); + } + }); From b3ca51c7d0eb172ba2525b0c14cd8e9caaec4d1f Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Thu, 16 Jul 2020 13:00:37 +0300 Subject: [PATCH 082/167] feat(prejoin): Add settings options for prejoin page --- css/modals/settings/_settings.scss | 6 ++- lang/main.json | 6 ++- react/features/settings/actions.js | 15 +++++++ .../settings/components/web/MoreTab.js | 45 ++++++++++++++++++- .../settings/components/web/SettingsDialog.js | 5 ++- react/features/settings/functions.js | 2 + 6 files changed, 72 insertions(+), 7 deletions(-) diff --git a/css/modals/settings/_settings.scss b/css/modals/settings/_settings.scss index 9a81dffa5..d4545ea6f 100644 --- a/css/modals/settings/_settings.scss +++ b/css/modals/settings/_settings.scss @@ -30,10 +30,12 @@ width: 100%; } - .profile-edit-field, - .settings-sub-pane { + .profile-edit-field { flex: 1; } + .settings-sub-pane { + flex-grow: 1; + } .profile-edit-field { margin-right: 20px; diff --git a/lang/main.json b/lang/main.json index 4758bc7dd..bd326a158 100644 --- a/lang/main.json +++ b/lang/main.json @@ -501,6 +501,7 @@ "audioAndVideoError": "Audio and video error:", "audioOnlyError": "Audio error:", "audioTrackError": "Could not create audio track.", + "calling": "Calling", "callMe": "Call me", "callMeAtNumber": "Call me at this number:", "configuringDevices": "Configuring devices...", @@ -524,7 +525,8 @@ "linkCopied": "Link copied to clipboard", "lookGood": "It sounds like your microphone is working properly", "or": "or", - "calling": "Calling", + "premeeting": "Pre meeting", + "showScreen": "Enable pre meeting screen", "startWithPhone": "Start with phone audio", "screenSharingError": "Screen sharing error:", "videoOnlyError": "Video error:", @@ -869,7 +871,7 @@ "header": "Help center" }, "lobby": { - "knockingParticipantList" : "Knocking participant list", + "knockingParticipantList": "Knocking participant list", "allow": "Allow", "backToKnockModeButton": "No password, ask to join instead", "dialogTitle": "Lobby mode", diff --git a/react/features/settings/actions.js b/react/features/settings/actions.js index 1138655ed..a88943496 100644 --- a/react/features/settings/actions.js +++ b/react/features/settings/actions.js @@ -3,6 +3,8 @@ import { setFollowMe, setStartMutedPolicy } from '../base/conference'; import { openDialog } from '../base/dialog'; import { i18next } from '../base/i18n'; +import { updateSettings } from '../base/settings'; +import { setPrejoinPageVisibility } from '../prejoin'; import { SET_AUDIO_SETTINGS_VISIBILITY, @@ -64,6 +66,19 @@ export function submitMoreTab(newState: Object): Function { dispatch(setFollowMe(newState.followMeEnabled)); } + const showPrejoinPage = newState.showPrejoinPage; + + if (showPrejoinPage !== currentState.showPrejoinPage) { + // The 'showPrejoin' flag starts as 'true' on every new session. + // This prevents displaying the prejoin page when the user re-enables it. + if (showPrejoinPage && getState()['features/prejoin']?.showPrejoin) { + dispatch(setPrejoinPageVisibility(false)); + } + dispatch(updateSettings({ + userSelectedSkipPrejoin: !showPrejoinPage + })); + } + if (newState.startAudioMuted !== currentState.startAudioMuted || newState.startVideoMuted !== currentState.startVideoMuted) { dispatch(setStartMutedPolicy( diff --git a/react/features/settings/components/web/MoreTab.js b/react/features/settings/components/web/MoreTab.js index 173714156..444ed52ed 100644 --- a/react/features/settings/components/web/MoreTab.js +++ b/react/features/settings/components/web/MoreTab.js @@ -48,6 +48,16 @@ export type Props = { */ showModeratorSettings: boolean, + /** + * Whether or not to display the prejoin settings section. + */ + showPrejoinSettings: boolean, + + /** + * Whether or not to show prejoin screen. + */ + showPrejoinPage: boolean, + /** * Whether or not the user has selected the Start Audio Muted feature to be * enabled. @@ -108,9 +118,13 @@ class MoreTab extends AbstractDialogTab { * @returns {ReactElement} */ render() { - const { showModeratorSettings, showLanguageSettings } = this.props; + const { showModeratorSettings, showLanguageSettings, showPrejoinSettings } = this.props; const content = []; + if (showPrejoinSettings) { + content.push(this._renderPrejoinScreenSettings()); + } + if (showModeratorSettings) { content.push(this._renderModeratorSettings()); } @@ -239,6 +253,35 @@ class MoreTab extends AbstractDialogTab {
); } + + /** + * Returns the React Element for modifying prejoin screen settings. + * + * @private + * @returns {ReactElement} + */ + _renderPrejoinScreenSettings() { + const { t, showPrejoinPage } = this.props; + + return ( +
+
+ { t('prejoin.premeeting') } +
+ + super._onChange({ showPrejoinPage: checked }) + } /> +
+ ); + } } export default translate(MoreTab); diff --git a/react/features/settings/components/web/SettingsDialog.js b/react/features/settings/components/web/SettingsDialog.js index 7a0633dbd..b361da6da 100644 --- a/react/features/settings/components/web/SettingsDialog.js +++ b/react/features/settings/components/web/SettingsDialog.js @@ -131,7 +131,7 @@ function _mapStateToProps(state) { // The settings sections to display. const showDeviceSettings = configuredTabs.includes('devices'); const moreTabProps = getMoreTabProps(state); - const { showModeratorSettings, showLanguageSettings } = moreTabProps; + const { showModeratorSettings, showLanguageSettings, showPrejoinSettings } = moreTabProps; const showProfileSettings = configuredTabs.includes('profile') && jwt.isGuest; const showCalendarSettings @@ -184,7 +184,7 @@ function _mapStateToProps(state) { }); } - if (showModeratorSettings || showLanguageSettings) { + if (showModeratorSettings || showLanguageSettings || showPrejoinSettings) { tabs.push({ name: SETTINGS_TABS.MORE, component: MoreTab, @@ -197,6 +197,7 @@ function _mapStateToProps(state) { ...newProps, currentLanguage: tabState.currentLanguage, followMeEnabled: tabState.followMeEnabled, + showPrejoinPage: tabState.showPrejoinPage, startAudioMuted: tabState.startAudioMuted, startVideoMuted: tabState.startVideoMuted }; diff --git a/react/features/settings/functions.js b/react/features/settings/functions.js index 2dc7e881a..36d4b7050 100644 --- a/react/features/settings/functions.js +++ b/react/features/settings/functions.js @@ -118,6 +118,8 @@ export function getMoreTabProps(stateful: Object | Function) { languages: LANGUAGES, showLanguageSettings: configuredTabs.includes('language'), showModeratorSettings, + showPrejoinSettings: state['features/base/config'].prejoinPageEnabled, + showPrejoinPage: !state['features/base/settings'].userSelectedSkipPrejoin, startAudioMuted: Boolean(conference && startAudioMutedPolicy), startVideoMuted: Boolean(conference && startVideoMutedPolicy) }; From 335b43036dd1a399abab466ef22b9c1b14eaa627 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 16 Apr 2020 02:06:11 +0100 Subject: [PATCH 083/167] Improve accessibility of Buttons in Webapp Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../base/toolbox/components/AbstractButton.js | 7 +-- .../toolbox/components/ToolboxItem.web.js | 46 ++++++++++++++++++- .../toolbox/components/web/ToolbarButton.js | 41 ++++++++++++++++- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/react/features/base/toolbox/components/AbstractButton.js b/react/features/base/toolbox/components/AbstractButton.js index ded2269d5..c0af079d9 100644 --- a/react/features/base/toolbox/components/AbstractButton.js +++ b/react/features/base/toolbox/components/AbstractButton.js @@ -230,13 +230,14 @@ export default class AbstractButton extends Component { /** * Helper function to be implemented by subclasses, which must return a - * {@code boolean} value indicating if this button is toggled or not. + * {@code boolean} value indicating if this button is toggled or not or + * undefined if the button is not toggleable. * * @protected - * @returns {boolean} + * @returns {?boolean} */ _isToggled() { - return false; + return undefined; } _onClick: (*) => void; diff --git a/react/features/base/toolbox/components/ToolboxItem.web.js b/react/features/base/toolbox/components/ToolboxItem.web.js index 27bfe3e3a..b57220d6b 100644 --- a/react/features/base/toolbox/components/ToolboxItem.web.js +++ b/react/features/base/toolbox/components/ToolboxItem.web.js @@ -12,6 +12,41 @@ import type { Props } from './AbstractToolboxItem'; * Web implementation of {@code AbstractToolboxItem}. */ export default class ToolboxItem extends AbstractToolboxItem { + /** + * Initializes a new {@code ToolboxItem} instance. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._onKeyDown = this._onKeyDown.bind(this); + } + + _onKeyDown: (Object) => void; + + /** + * Handles 'Enter' key on the button to trigger onClick for accessibility. + * We should be handling Space onKeyUp but it conflicts with PTT. + * + * @param {Object} event - The key event. + * @private + * @returns {void} + */ + _onKeyDown(event) { + // If the event coming to the dialog has been subject to preventDefault + // we don't handle it here. + if (event.defaultPrevented) { + return; + } + + if (event.key === 'Enter') { + event.preventDefault(); + event.stopPropagation(); + this.props.onClick(); + } + } + /** * Handles rendering of the actual item. If the label is being shown, which * is controlled with the `showLabel` prop, the item is rendered for its @@ -27,14 +62,21 @@ export default class ToolboxItem extends AbstractToolboxItem { elementAfter, onClick, showLabel, - tooltipPosition + tooltipPosition, + toggled } = this.props; const className = showLabel ? 'overflow-menu-item' : 'toolbox-button'; const props = { + 'aria-pressed': toggled, + 'aria-disabled': disabled, 'aria-label': this.accessibilityLabel, className: className + (disabled ? ' disabled' : ''), - onClick: disabled ? undefined : onClick + onClick: disabled ? undefined : onClick, + onKeyDown: this._onKeyDown, + tabIndex: 0, + role: 'button' }; + const elementType = showLabel ? 'li' : 'div'; const useTooltip = this.tooltip && this.tooltip.length > 0; let children = ( diff --git a/react/features/toolbox/components/web/ToolbarButton.js b/react/features/toolbox/components/web/ToolbarButton.js index e3d299861..7ecb1fbd7 100644 --- a/react/features/toolbox/components/web/ToolbarButton.js +++ b/react/features/toolbox/components/web/ToolbarButton.js @@ -40,6 +40,41 @@ class ToolbarButton extends AbstractToolbarButton { tooltipPosition: 'top' }; + /** + * Initializes a new {@code ToolbarButton} instance. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._onKeyDown = this._onKeyDown.bind(this); + } + + _onKeyDown: (Object) => void; + + /** + * Handles 'Enter' key on the button to trigger onClick for accessibility. + * We should be handling Space onKeyUp but it conflicts with PTT. + * + * @param {Object} event - The key event. + * @private + * @returns {void} + */ + _onKeyDown(event) { + // If the event coming to the dialog has been subject to preventDefault + // we don't handle it here. + if (event.defaultPrevented) { + return; + } + + if (event.key === 'Enter') { + event.preventDefault(); + event.stopPropagation(); + this.props.onClick(); + } + } + /** * Renders the button of this {@code ToolbarButton}. * @@ -52,8 +87,12 @@ class ToolbarButton extends AbstractToolbarButton { return (
+ onClick = { this.props.onClick } + onKeyDown = { this._onKeyDown } + role = 'button' + tabIndex = { 0 }> { this.props.tooltip ? Date: Fri, 17 Jul 2020 16:19:31 +0200 Subject: [PATCH 084/167] deps: lib-jitsi-meet@latest --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb19f32a4..f264be5ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10725,8 +10725,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", - "from": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", + "version": "github:jitsi/lib-jitsi-meet#e2626b2d75058dc936ad5d588bf327e1197caed5", + "from": "github:jitsi/lib-jitsi-meet#e2626b2d75058dc936ad5d588bf327e1197caed5", "requires": { "@jitsi/js-utils": "1.0.0", "@jitsi/sdp-interop": "1.0.3", diff --git a/package.json b/package.json index f20bb8e78..cfb385c2a 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "jquery-i18next": "1.2.1", "js-md5": "0.6.1", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#ff1813cbb2e7d36f1cc0bbc2b8baa0a16c6346a4", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#e2626b2d75058dc936ad5d588bf327e1197caed5", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.19", "moment": "2.19.4", From f2c3401a79842bbfe0aab4a31e6390a7fffe985d Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Mon, 20 Jul 2020 11:43:07 -0500 Subject: [PATCH 085/167] chore: Update lib-jitsi-meet. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f264be5ae..0e9b866d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10725,8 +10725,8 @@ } }, "lib-jitsi-meet": { - "version": "github:jitsi/lib-jitsi-meet#e2626b2d75058dc936ad5d588bf327e1197caed5", - "from": "github:jitsi/lib-jitsi-meet#e2626b2d75058dc936ad5d588bf327e1197caed5", + "version": "github:jitsi/lib-jitsi-meet#2c4e3816e97d174b0a9e82ce1ace5a77eda9a891", + "from": "github:jitsi/lib-jitsi-meet#2c4e3816e97d174b0a9e82ce1ace5a77eda9a891", "requires": { "@jitsi/js-utils": "1.0.0", "@jitsi/sdp-interop": "1.0.3", diff --git a/package.json b/package.json index cfb385c2a..34c89bb6b 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "jquery-i18next": "1.2.1", "js-md5": "0.6.1", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#e2626b2d75058dc936ad5d588bf327e1197caed5", + "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#2c4e3816e97d174b0a9e82ce1ace5a77eda9a891", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "lodash": "4.17.19", "moment": "2.19.4", From e39c8d8ed61e31ee65b397826b1f349ec01e64fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Thu, 16 Jul 2020 17:41:15 +0200 Subject: [PATCH 086/167] rn,tile-view: render a larger avatar in tile view mode --- react/features/filmstrip/components/native/Thumbnail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/features/filmstrip/components/native/Thumbnail.js b/react/features/filmstrip/components/native/Thumbnail.js index 03e29b704..288985ae5 100644 --- a/react/features/filmstrip/components/native/Thumbnail.js +++ b/react/features/filmstrip/components/native/Thumbnail.js @@ -149,7 +149,7 @@ function Thumbnail(props: Props) { touchFeedback = { false }> Date: Thu, 16 Jul 2020 17:41:43 +0200 Subject: [PATCH 087/167] rn,tile-view: render display name on top of participant view Makes the tiles consistent as the participant view is equally sized and the avatar is always centered in the tile. --- .../conference/components/native/Conference.js | 4 +++- react/features/conference/components/native/styles.js | 4 ++++ .../components/native/DisplayNameLabel.js | 2 +- .../features/display-name/components/native/styles.js | 1 - .../features/filmstrip/components/native/Thumbnail.js | 4 +++- react/features/filmstrip/components/native/styles.js | 11 +++++++++++ 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/react/features/conference/components/native/Conference.js b/react/features/conference/components/native/Conference.js index b3cd5984f..d7960b598 100644 --- a/react/features/conference/components/native/Conference.js +++ b/react/features/conference/components/native/Conference.js @@ -295,7 +295,9 @@ class Conference extends AbstractConference { - { _shouldDisplayTileView || } + { _shouldDisplayTileView || + + } diff --git a/react/features/conference/components/native/styles.js b/react/features/conference/components/native/styles.js index d5ffaa06d..83b618b50 100644 --- a/react/features/conference/components/native/styles.js +++ b/react/features/conference/components/native/styles.js @@ -33,6 +33,10 @@ export default { flex: 1 }), + displayNameContainer: { + margin: 10 + }, + gradient: { position: 'absolute', top: 0, diff --git a/react/features/display-name/components/native/DisplayNameLabel.js b/react/features/display-name/components/native/DisplayNameLabel.js index 04e274763..eba28f712 100644 --- a/react/features/display-name/components/native/DisplayNameLabel.js +++ b/react/features/display-name/components/native/DisplayNameLabel.js @@ -73,7 +73,7 @@ function _mapStateToProps(state: Object, ownProps: Props) { // participant and there is no video rendered for // them. const _render = Boolean(participantId) - && localParticipant.id !== participantId + && localParticipant?.id !== participantId && !shouldRenderParticipantVideo(state, participantId) && !isFakeParticipant; diff --git a/react/features/display-name/components/native/styles.js b/react/features/display-name/components/native/styles.js index 79efe2a06..db4a3283d 100644 --- a/react/features/display-name/components/native/styles.js +++ b/react/features/display-name/components/native/styles.js @@ -7,7 +7,6 @@ export default { alignSelf: 'center', backgroundColor: 'rgba(28, 32, 37, 0.6)', borderRadius: 4, - margin: 16, paddingHorizontal: 16, paddingVertical: 4 }, diff --git a/react/features/filmstrip/components/native/Thumbnail.js b/react/features/filmstrip/components/native/Thumbnail.js index 288985ae5..26f9429a4 100644 --- a/react/features/filmstrip/components/native/Thumbnail.js +++ b/react/features/filmstrip/components/native/Thumbnail.js @@ -157,7 +157,9 @@ function Thumbnail(props: Props) { tintStyle = { _styles.activeThumbnailTint } zOrder = { 1 } /> - { renderDisplayName && } + { renderDisplayName && + + } { renderModeratorIndicator && diff --git a/react/features/filmstrip/components/native/styles.js b/react/features/filmstrip/components/native/styles.js index 42d7f05b8..3eda154a8 100644 --- a/react/features/filmstrip/components/native/styles.js +++ b/react/features/filmstrip/components/native/styles.js @@ -14,6 +14,17 @@ export const AVATAR_SIZE = 50; */ export default { + /** + * The display name container. + */ + displayNameContainer: { + alignSelf: 'center', + bottom: 0, + flex: 1, + margin: 4, + position: 'absolute' + }, + /** * The style of the narrow {@link Filmstrip} version which displays * thumbnails in a row at the bottom of the screen. From 0751c6ab48d9e691f866da52446fb0b64ee02c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 13:14:37 +0200 Subject: [PATCH 088/167] ios: fix uploading dSYMs to Crashlytics Use Fastlane to get them from Apple after processing, them upload them. Also make sure WebRTC dSYMs are included when uploading. --- ios/app/app.xcodeproj/project.pbxproj | 38 +++++++++++++-------------- ios/fastlane/Fastfile | 4 +++ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/ios/app/app.xcodeproj/project.pbxproj b/ios/app/app.xcodeproj/project.pbxproj index 3b8158608..22bd9d96d 100644 --- a/ios/app/app.xcodeproj/project.pbxproj +++ b/ios/app/app.xcodeproj/project.pbxproj @@ -291,9 +291,9 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */, B35383AD1DDA0083008F406A /* Adjust embedded framework architectures */, + DE3A859324C701EA009B7D76 /* Copy WebRTC dSYM */, 0BB7DA181EC9E695007AAE98 /* Adjust ATS */, DEF4813D224925A2002AD03A /* Copy Google Plist file */, - DEC2069321CBBD6900072F03 /* Setup Crashlytics */, DE11877A21EE09640078D059 /* Setup Google reverse URL handler */, DE4F6D6E22005C0400DE699E /* Setup Dropbox */, 0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */, @@ -474,6 +474,24 @@ shellPath = /bin/sh; shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nGOOGLE_PLIST=\"$PROJECT_DIR/GoogleService-Info.plist\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n"; }; + DE3A859324C701EA009B7D76 /* Copy WebRTC dSYM */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy WebRTC dSYM"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -x\n\nif [[ \"${CONFIGURATION}\" != \"Debug\" ]]; then\n cp -r ../../node_modules/react-native-webrtc/ios/WebRTC.dSYM ${DWARF_DSYM_FOLDER_PATH}/\nfi\n"; + }; DE4F6D6E22005C0400DE699E /* Setup Dropbox */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -492,24 +510,6 @@ shellPath = /bin/sh; shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nDROPBOX_KEY_FILE=\"$PROJECT_DIR/dropbox.key\"\n\nif [[ -f $DROPBOX_KEY_FILE ]]; then\n /usr/libexec/PlistBuddy -c \"Delete :LSApplicationQueriesSchemes\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:0 string 'dbapi-2'\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:1 string 'dbapi-8-emm'\" $INFO_PLIST\n\n DROPBOX_KEY=$(head -n 1 $DROPBOX_KEY_FILE)\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLName string dropbox\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes:0 string $DROPBOX_KEY\" $INFO_PLIST\nfi\n"; }; - DEC2069321CBBD6900072F03 /* Setup Crashlytics */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Setup Crashlytics"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "GOOGLE_PLIST=\"$PROJECT_DIR/GoogleService-Info.plist\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n if [ \"${CONFIGURATION}\" != \"Debug\" ]; then\n find \"${DWARF_DSYM_FOLDER_PATH}\" -name \"*.dSYM\" | xargs -I \\{\\} ${PODS_ROOT}/Fabric/upload-symbols -gsp $GOOGLE_PLIST -p ios \\{\\}\n fi\nfi\n"; - }; DEF4813D224925A2002AD03A /* Copy Google Plist file */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 20ec61e4d..f7eda50e4 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -80,6 +80,10 @@ platform :ios do uses_non_exempt_encryption: false ) + # Upload dSYMs to Crashlytics + download_dsyms + upload_symbols_to_crashlytics + # Cleanup clean_build_artifacts reset_git_repo(skip_clean: true) From ddaaeccafa81e92ad47c3b4497dd1df09f8e9e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 11:13:24 +0200 Subject: [PATCH 089/167] android: disable PiP on Android Go devices Despite running Android 8.1, they don't support Picture-in-Picture. --- .../org/jitsi/meet/sdk/JitsiMeetView.java | 5 +-- .../meet/sdk/PictureInPictureModule.java | 42 +++++++++++++++---- .../components/PictureInPictureButton.js | 7 ++-- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java index 9c1bb2bd0..e69004611 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java @@ -1,6 +1,5 @@ /* - * Copyright @ 2018-present 8x8, Inc. - * Copyright @ 2017-2018 Atlassian Pty Ltd + * Copyright @ 2017-present 8x8, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -126,7 +125,7 @@ public class JitsiMeetView extends BaseReactView = ReactInstanceManagerHolder.getNativeModule( PictureInPictureModule.class); if (pipModule != null - && PictureInPictureModule.isPictureInPictureSupported() + && pipModule.isPictureInPictureSupported() && !JitsiMeetActivityDelegate.arePermissionsBeingRequested() && this.url != null) { try { diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/PictureInPictureModule.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/PictureInPictureModule.java index 2123334d5..a8edcd957 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/PictureInPictureModule.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/PictureInPictureModule.java @@ -1,5 +1,5 @@ /* - * Copyright @ 2017-present Atlassian Pty Ltd + * Copyright @ 2017-present 8x8, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.jitsi.meet.sdk; import android.annotation.TargetApi; import android.app.Activity; +import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.os.Build; import android.util.Rational; @@ -30,20 +31,41 @@ import com.facebook.react.module.annotations.ReactModule; import org.jitsi.meet.sdk.log.JitsiMeetLogger; +import java.util.HashMap; +import java.util.Map; + +import static android.content.Context.ACTIVITY_SERVICE; + @ReactModule(name = PictureInPictureModule.NAME) -class PictureInPictureModule - extends ReactContextBaseJavaModule { +class PictureInPictureModule extends ReactContextBaseJavaModule { public static final String NAME = "PictureInPicture"; - private static final String TAG = NAME; - static boolean isPictureInPictureSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; - } + private static boolean isSupported; public PictureInPictureModule(ReactApplicationContext reactContext) { super(reactContext); + + ActivityManager am = (ActivityManager) reactContext.getSystemService(ACTIVITY_SERVICE); + + // Android Go devices don't support PiP. There doesn't seem to be a better way to detect it than + // to use ActivityManager.isLowRamDevice(). + // https://stackoverflow.com/questions/58340558/how-to-detect-android-go + isSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !am.isLowRamDevice(); + } + + /** + * Gets a {@code Map} of constants this module exports to JS. Supports JSON + * types. + * + * @return a {@link Map} of constants this module exports to JS + */ + @Override + public Map getConstants() { + Map constants = new HashMap<>(); + constants.put("SUPPORTED", isSupported); + return constants; } /** @@ -61,7 +83,7 @@ class PictureInPictureModule */ @TargetApi(Build.VERSION_CODES.O) public void enterPictureInPicture() { - if (!isPictureInPictureSupported()) { + if (!isSupported) { throw new IllegalStateException("Picture-in-Picture not supported"); } @@ -104,6 +126,10 @@ class PictureInPictureModule } } + public boolean isPictureInPictureSupported() { + return isSupported; + } + @Override public String getName() { return NAME; diff --git a/react/features/mobile/picture-in-picture/components/PictureInPictureButton.js b/react/features/mobile/picture-in-picture/components/PictureInPictureButton.js index 2212b7d1b..63220a56e 100644 --- a/react/features/mobile/picture-in-picture/components/PictureInPictureButton.js +++ b/react/features/mobile/picture-in-picture/components/PictureInPictureButton.js @@ -1,6 +1,6 @@ // @flow -import { Platform } from 'react-native'; +import { NativeModules, Platform } from 'react-native'; import { PIP_ENABLED, getFeatureFlag } from '../../../base/flags'; import { translate } from '../../../base/i18n'; @@ -66,9 +66,8 @@ function _mapStateToProps(state): Object { const flag = Boolean(getFeatureFlag(state, PIP_ENABLED)); let enabled = flag; - // Override flag for Android < 26, PiP was introduced in Oreo. - // https://developer.android.com/guide/topics/ui/picture-in-picture - if (Platform.OS === 'android' && Platform.Version < 26) { + // Override flag for Android, since it might be unsupported. + if (Platform.OS === 'android' && !NativeModules.PictureInPicture.SUPPORTED) { enabled = false; } From 3cbadc72a1aaebb65e0b5973872ace9c319749f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 14:19:07 +0200 Subject: [PATCH 090/167] analytics: only import required constant --- react/features/analytics/functions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/features/analytics/functions.js b/react/features/analytics/functions.js index 5ef0dc793..ab87fbd34 100644 --- a/react/features/analytics/functions.js +++ b/react/features/analytics/functions.js @@ -1,6 +1,6 @@ // @flow -import { API_ID } from '../../../modules/API'; +import { API_ID } from '../../../modules/API/constants'; import { checkChromeExtensionsInstalled, isMobileBrowser From 46b444c498d8477c48f741be07fcc638b4bec7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 14:19:54 +0200 Subject: [PATCH 091/167] settings: only import required action Fixes crash on mobile due to chained imports. --- react/features/settings/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/features/settings/actions.js b/react/features/settings/actions.js index a88943496..ac5110087 100644 --- a/react/features/settings/actions.js +++ b/react/features/settings/actions.js @@ -4,7 +4,7 @@ import { setFollowMe, setStartMutedPolicy } from '../base/conference'; import { openDialog } from '../base/dialog'; import { i18next } from '../base/i18n'; import { updateSettings } from '../base/settings'; -import { setPrejoinPageVisibility } from '../prejoin'; +import { setPrejoinPageVisibility } from '../prejoin/actions'; import { SET_AUDIO_SETTINGS_VISIBILITY, From fde7cf4ab83fde7e2053184f63e102fe7ea51212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 14:21:01 +0200 Subject: [PATCH 092/167] chat: fix crash on mobile --- .../chat/{actions.js => actions.any.js} | 18 +---------------- react/features/chat/actions.native.js | 16 +++++++++++++++ react/features/chat/actions.web.js | 20 +++++++++++++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) rename react/features/chat/{actions.js => actions.any.js} (83%) create mode 100644 react/features/chat/actions.native.js create mode 100644 react/features/chat/actions.web.js diff --git a/react/features/chat/actions.js b/react/features/chat/actions.any.js similarity index 83% rename from react/features/chat/actions.js rename to react/features/chat/actions.any.js index 99935c1db..0160ad783 100644 --- a/react/features/chat/actions.js +++ b/react/features/chat/actions.any.js @@ -1,13 +1,10 @@ // @flow -import VideoLayout from '../../../modules/UI/videolayout/VideoLayout'; - import { ADD_MESSAGE, CLEAR_MESSAGES, SEND_MESSAGE, - SET_PRIVATE_MESSAGE_RECIPIENT, - TOGGLE_CHAT + SET_PRIVATE_MESSAGE_RECIPIENT } from './actionTypes'; /** @@ -86,16 +83,3 @@ export function setPrivateMessageRecipient(participant: Object) { type: SET_PRIVATE_MESSAGE_RECIPIENT }; } - -/** - * Toggles display of the chat side panel while also taking window - * resize into account. - * - * @returns {Function} - */ -export function toggleChat() { - return function(dispatch: (Object) => Object) { - dispatch({ type: TOGGLE_CHAT }); - VideoLayout.onResize(); - }; -} diff --git a/react/features/chat/actions.native.js b/react/features/chat/actions.native.js new file mode 100644 index 000000000..247982d83 --- /dev/null +++ b/react/features/chat/actions.native.js @@ -0,0 +1,16 @@ +// @flow + +import { TOGGLE_CHAT } from './actionTypes'; + +export * from './actions.any'; + +/** + * Toggles display of the chat panel. + * + * @returns {Function} + */ +export function toggleChat() { + return function(dispatch: (Object) => Object) { + dispatch({ type: TOGGLE_CHAT }); + }; +} diff --git a/react/features/chat/actions.web.js b/react/features/chat/actions.web.js new file mode 100644 index 000000000..38c4f14a4 --- /dev/null +++ b/react/features/chat/actions.web.js @@ -0,0 +1,20 @@ +// @flow + +import VideoLayout from '../../../modules/UI/videolayout/VideoLayout'; + +import { TOGGLE_CHAT } from './actionTypes'; + +export * from './actions.any'; + +/** + * Toggles display of the chat side panel while also taking window + * resize into account. + * + * @returns {Function} + */ +export function toggleChat() { + return function(dispatch: (Object) => Object) { + dispatch({ type: TOGGLE_CHAT }); + VideoLayout.onResize(); + }; +} From eaa715879a9168f794bb790a7ebf85976626004b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 15:36:11 +0200 Subject: [PATCH 093/167] rn: update versions --- android/gradle.properties | 4 ++-- ios/app/src/Info.plist | 2 +- ios/app/watchos/app/Info.plist | 2 +- ios/app/watchos/extension/Info.plist | 2 +- ios/sdk/src/Info.plist | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 9df2468f5..d9dad0546 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -20,5 +20,5 @@ android.useAndroidX=true android.enableJetifier=true -appVersion=20.3.0 -sdkVersion=2.9.0 +appVersion=20.4.0 +sdkVersion=2.10.0 diff --git a/ios/app/src/Info.plist b/ios/app/src/Info.plist index 3b69134ed..3bd1c3c61 100644 --- a/ios/app/src/Info.plist +++ b/ios/app/src/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 20.3.0 + 20.4.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/app/watchos/app/Info.plist b/ios/app/watchos/app/Info.plist index b3088b492..8db9899e4 100644 --- a/ios/app/watchos/app/Info.plist +++ b/ios/app/watchos/app/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 20.3.0 + 20.4.0 CFBundleVersion 1 UISupportedInterfaceOrientations diff --git a/ios/app/watchos/extension/Info.plist b/ios/app/watchos/extension/Info.plist index 46bcf4141..15576d4e5 100644 --- a/ios/app/watchos/extension/Info.plist +++ b/ios/app/watchos/extension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 20.3.0 + 20.4.0 CFBundleVersion 1 CLKComplicationPrincipalClass diff --git a/ios/sdk/src/Info.plist b/ios/sdk/src/Info.plist index 9d8a4ae9a..ca8ede28e 100644 --- a/ios/sdk/src/Info.plist +++ b/ios/sdk/src/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.9.0 + 2.10.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From eb1ef0fa9c0755c85665336b5a84814476a4ffed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BA=B7ng=20Minh=20Ti=E1=BA=BFn?= Date: Tue, 21 Jul 2020 21:01:38 +0700 Subject: [PATCH 094/167] Update JitsiStreamPresenterEffect.js (#7362) * Update JitsiStreamPresenterEffect.js Create/terminate the Web Worker on effect start/stop so that we don't leak them. --- .../stream-effects/presenter/JitsiStreamPresenterEffect.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/react/features/stream-effects/presenter/JitsiStreamPresenterEffect.js b/react/features/stream-effects/presenter/JitsiStreamPresenterEffect.js index 5943616c0..a1b6c43ed 100644 --- a/react/features/stream-effects/presenter/JitsiStreamPresenterEffect.js +++ b/react/features/stream-effects/presenter/JitsiStreamPresenterEffect.js @@ -64,8 +64,6 @@ export default class JitsiStreamPresenterEffect { // Bind event handler so it is only bound once for every instance. this._onVideoFrameTimer = this._onVideoFrameTimer.bind(this); - this._videoFrameTimerWorker = new Worker(timerWorkerScript, { name: 'Presenter effect worker' }); - this._videoFrameTimerWorker.onmessage = this._onVideoFrameTimer; } /** @@ -136,6 +134,8 @@ export default class JitsiStreamPresenterEffect { this._desktopElement.srcObject = desktopStream; this._canvas.width = parseInt(width, 10); this._canvas.height = parseInt(height, 10); + this._videoFrameTimerWorker = new Worker(timerWorkerScript, { name: 'Presenter effect worker' }); + this._videoFrameTimerWorker.onmessage = this._onVideoFrameTimer; this._videoFrameTimerWorker.postMessage({ id: SET_INTERVAL, timeMs: 1000 / this._frameRate @@ -153,6 +153,7 @@ export default class JitsiStreamPresenterEffect { this._videoFrameTimerWorker.postMessage({ id: CLEAR_INTERVAL }); + this._videoFrameTimerWorker.terminate(); } } From 48a58f8dae4bbe35956ae7aa7c9a9b0e4bfcbf1e Mon Sep 17 00:00:00 2001 From: Dominik Wagner Date: Tue, 21 Jul 2020 17:06:41 +0200 Subject: [PATCH 095/167] ios: specify the correct keyboard type and content This way autocomplete and keyboards work correctly. --- react/features/settings/components/native/SettingsView.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/react/features/settings/components/native/SettingsView.js b/react/features/settings/components/native/SettingsView.js index c5decf726..2a7a6d50a 100644 --- a/react/features/settings/components/native/SettingsView.js +++ b/react/features/settings/components/native/SettingsView.js @@ -160,6 +160,7 @@ class SettingsView extends AbstractSettingsView { autoCorrect = { false } onChangeText = { this._onChangeDisplayName } placeholder = 'John Doe' + textContentType = { 'name' } // iOS only value = { displayName } /> { keyboardType = { 'email-address' } onChangeText = { this._onChangeEmail } placeholder = 'email@example.com' + textContentType = { 'emailAddress' } // iOS only value = { email } /> { autoCapitalize = 'none' autoCorrect = { false } editable = { this.props._serverURLChangeEnabled } + keyboardType = { 'url' } onBlur = { this._onBlurServerURL } onChangeText = { this._onChangeServerURL } placeholder = { this.props._serverURL } + textContentType = { 'URL' } // iOS only value = { serverURL } /> { - { `${AppInfo.version} build ${AppInfo.buildNumber}` } + {`${AppInfo.version} build ${AppInfo.buildNumber}`} - { this._renderAdvancedSettings() } + {this._renderAdvancedSettings()} ); From 9d6e21b77bf948f4a2d932d03931a9f3daa4e234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Jul 2020 17:17:43 +0200 Subject: [PATCH 096/167] security-dialog: restore digit-only password functionality --- .../security/components/security-dialog/SecurityDialog.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/react/features/security/components/security-dialog/SecurityDialog.js b/react/features/security/components/security-dialog/SecurityDialog.js index 4d24cb837..51016b771 100644 --- a/react/features/security/components/security-dialog/SecurityDialog.js +++ b/react/features/security/components/security-dialog/SecurityDialog.js @@ -125,13 +125,18 @@ function mapStateToProps(state) { locked, password } = state['features/base/conference']; + const { + lockRoomGuestEnabled, + roomPasswordNumberOfDigits + } = state['features/base/config']; return { - _canEditPassword: isLocalParticipantModerator(state, state['features/base/config'].lockRoomGuestEnabled), + _canEditPassword: isLocalParticipantModerator(state, lockRoomGuestEnabled), _conference: conference, _dialIn: state['features/invite'], _locked: locked, _password: password, + _passwordNumberOfDigits: roomPasswordNumberOfDigits, _showE2ee: Boolean(e2eeSupported) }; } From 1e3e15fc72eb3dde5bea0f6947628c35e5826785 Mon Sep 17 00:00:00 2001 From: Gabriel Imre Date: Tue, 21 Jul 2020 11:26:26 +0300 Subject: [PATCH 097/167] fix: use consistent moderator semantics Use the same moderator semantics when adding items into the remote video menu as when showing/hiding the items themselves. --- .../components/AbstractGrantModeratorButton.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js b/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js index 426f71890..af49c0773 100644 --- a/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js +++ b/react/features/remote-video-menu/components/AbstractGrantModeratorButton.js @@ -3,9 +3,10 @@ import { openDialog } from '../../base/dialog'; import { IconCrown } from '../../base/icons'; import { + getLocalParticipant, getParticipantById, - isLocalParticipantModerator, - isParticipantModerator + isParticipantModerator, + PARTICIPANT_ROLE } from '../../base/participants'; import { AbstractButton } from '../../base/toolbox'; import type { AbstractButtonProps } from '../../base/toolbox'; @@ -64,7 +65,11 @@ export default class AbstractGrantModeratorButton extends AbstractButton Date: Wed, 22 Jul 2020 12:50:10 +0200 Subject: [PATCH 098/167] rn: fix overriding user-selected server URL Fixes: https://github.com/jitsi/jitsi-meet/issues/7373 --- react/features/app/components/App.native.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/react/features/app/components/App.native.js b/react/features/app/components/App.native.js index 5b692ac18..00b255861 100644 --- a/react/features/app/components/App.native.js +++ b/react/features/app/components/App.native.js @@ -4,7 +4,9 @@ import React from 'react'; import { setColorScheme } from '../../base/color-scheme'; import { DialogContainer } from '../../base/dialog'; -import { CALL_INTEGRATION_ENABLED, SERVER_URL_CHANGE_ENABLED, updateFlags } from '../../base/flags'; +import { updateFlags } from '../../base/flags/actions'; +import { CALL_INTEGRATION_ENABLED, SERVER_URL_CHANGE_ENABLED } from '../../base/flags/constants'; +import { getFeatureFlag } from '../../base/flags/functions'; import { Platform } from '../../base/react'; import { DimensionsDetector, clientResized } from '../../base/responsive-ui'; import { updateSettings } from '../../base/settings'; @@ -83,11 +85,14 @@ export class App extends AbstractApp { super.componentDidMount(); this._init.then(() => { + const { dispatch, getState } = this.state.store; + // We set these early enough so then we avoid any unnecessary re-renders. - const { dispatch } = this.state.store; + dispatch(setColorScheme(this.props.colorScheme)); + dispatch(updateFlags(this.props.flags)); // Check if serverURL is configured externally and not allowed to change. - const serverURLChangeEnabled = this.props.flags[SERVER_URL_CHANGE_ENABLED]; + const serverURLChangeEnabled = getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true); if (!serverURLChangeEnabled) { // As serverURL is provided externally, so we push it to settings. @@ -100,8 +105,6 @@ export class App extends AbstractApp { } } - dispatch(setColorScheme(this.props.colorScheme)); - dispatch(updateFlags(this.props.flags)); dispatch(updateSettings(this.props.userInfo || {})); // Update settings with feature-flag. From b831bb8350184c89a1cef094e460ccddf5a70a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 22 Jul 2020 10:38:33 +0200 Subject: [PATCH 099/167] config: add roomPasswordNumberOfDigits to whitelist --- react/features/base/config/configWhitelist.js | 1 + 1 file changed, 1 insertion(+) diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 12d19631b..59bf8b45b 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -132,6 +132,7 @@ export default [ 'prejoinPageEnabled', 'requireDisplayName', 'remoteVideoMenu', + 'roomPasswordNumberOfDigits', 'resolution', 'startAudioMuted', 'startAudioOnly', From 0fc748dc4490d3da51c2d45c61ec38904ae910b5 Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Thu, 16 Jul 2020 16:59:26 +0300 Subject: [PATCH 100/167] ui: create reusable copy button --- css/buttons/copy.scss | 37 ++++++ css/main.scss | 1 + css/modals/invite/_invite_more.scss | 38 ------ react/features/base/buttons/CopyButton.js | 125 ++++++++++++++++++ .../web/CopyMeetingLinkSection.js | 87 ++---------- 5 files changed, 172 insertions(+), 116 deletions(-) create mode 100644 css/buttons/copy.scss create mode 100644 react/features/base/buttons/CopyButton.js diff --git a/css/buttons/copy.scss b/css/buttons/copy.scss new file mode 100644 index 000000000..9596abcc3 --- /dev/null +++ b/css/buttons/copy.scss @@ -0,0 +1,37 @@ +.copy-button { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 8px 8px 16px; + margin-top: 8px; + width: calc(100% - 24px); + height: 24px; + + background: #0376DA; + border-radius: 4px; + cursor: pointer; + + &:hover { + background: #278ADF; + font-weight: 600; + } + + &-content { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 292px; + + &.selected { + font-weight: 600; + } + } + + &.clicked { + background: #31B76A; + } + + & > div > svg > path { + fill: #fff; + } +} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index 826fb9932..1257937f2 100644 --- a/css/main.scss +++ b/css/main.scss @@ -33,6 +33,7 @@ $flagsImagePath: "../images/"; @import 'inlay'; @import 'reload_overlay/reload_overlay'; @import 'mini_toolbox'; +@import 'buttons/copy.scss'; @import 'modals/desktop-picker/desktop-picker'; @import 'modals/device-selection/device-selection'; @import 'modals/dialog'; diff --git a/css/modals/invite/_invite_more.scss b/css/modals/invite/_invite_more.scss index 824ed7f6b..73160928a 100644 --- a/css/modals/invite/_invite_more.scss +++ b/css/modals/invite/_invite_more.scss @@ -67,44 +67,6 @@ } } - &.copy-link { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 8px 8px 16px; - margin-top: 8px; - width: calc(100% - 24px); - height: 24px; - - background: #0376DA; - border-radius: 4px; - cursor: pointer; - - &:hover { - background: #278ADF; - font-weight: 600; - } - - &-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 292px; - - &.selected { - font-weight: 600; - } - } - - &.clicked { - background: #31B76A; - } - - & > div > svg > path { - fill: #fff; - } - } - &.separator { margin: 24px 0 24px -20px; padding: 0 20px; diff --git a/react/features/base/buttons/CopyButton.js b/react/features/base/buttons/CopyButton.js new file mode 100644 index 000000000..a99ba7cc5 --- /dev/null +++ b/react/features/base/buttons/CopyButton.js @@ -0,0 +1,125 @@ +// @flow + +import React, { useState } from 'react'; + +import { translate } from '../../base/i18n'; +import { Icon, IconCheck, IconCopy } from '../../base/icons'; +import { copyText } from '../../base/util'; + + +type Props = { + + /** + * Css class to apply on container + */ + className: string, + + /** + * The displayed text + */ + displayedText: string, + + /** + * The text that needs to be copied (might differ from the displayedText) + */ + textToCopy: string, + + /** + * The text displayed on mouse hover + */ + textOnHover: string, + + /** + * The text displayed on copy success + */ + textOnCopySuccess: string +}; + +/** + * Component meant to enable users to copy the conference URL. + * + * @returns {React$Element} + */ +function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnCopySuccess }: Props) { + const [ isClicked, setIsClicked ] = useState(false); + const [ isHovered, setIsHovered ] = useState(false); + + /** + * Click handler for the element. + * + * @returns {void} + */ + function onClick() { + setIsHovered(false); + if (copyText(textToCopy)) { + setIsClicked(true); + + setTimeout(() => { + setIsClicked(false); + }, 2500); + } + } + + /** + * Hover handler for the element. + * + * @returns {void} + */ + function onHoverIn() { + if (!isClicked) { + setIsHovered(true); + } + } + + /** + * Hover handler for the element. + * + * @returns {void} + */ + function onHoverOut() { + setIsHovered(false); + } + + /** + * Renders the content of the link based on the state. + * + * @returns {React$Element} + */ + function renderContent() { + if (isClicked) { + return ( + <> +
+ {textOnCopySuccess} +
+ + + ); + } + + return ( + <> +
+ {isHovered ? textOnHover : displayedText} +
+ + + ); + } + + return ( +
+ { renderContent() } +
+ ); +} + +CopyButton.defaultProps = { + className: '' +}; + +export default translate(CopyButton); diff --git a/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js b/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js index 6dc327d80..4f07ddc0e 100644 --- a/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js +++ b/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js @@ -1,10 +1,10 @@ // @flow -import React, { useState } from 'react'; +import React from 'react'; +import CopyButton from '../../../../base/buttons/CopyButton'; import { translate } from '../../../../base/i18n'; -import { Icon, IconCheck, IconCopy } from '../../../../base/icons'; -import { copyText, getDecodedURI } from '../../../../base/util'; +import { getDecodedURI } from '../../../../base/util'; type Props = { @@ -26,84 +26,15 @@ type Props = { * @returns {React$Element} */ function CopyMeetingLinkSection({ t, url }: Props) { - const [ isClicked, setIsClicked ] = useState(false); - const [ isHovered, setIsHovered ] = useState(false); - - /** - * Click handler for the element. - * - * @returns {void} - */ - function onClick() { - setIsHovered(false); - if (copyText(url)) { - setIsClicked(true); - - setTimeout(() => { - setIsClicked(false); - }, 2500); - } - } - - /** - * Hover handler for the element. - * - * @returns {void} - */ - function onHoverIn() { - if (!isClicked) { - setIsHovered(true); - } - } - - /** - * Hover handler for the element. - * - * @returns {void} - */ - function onHoverOut() { - setIsHovered(false); - } - - /** - * Renders the content of the link based on the state. - * - * @returns {React$Element} - */ - function renderLinkContent() { - if (isClicked) { - return ( - <> -
- {t('addPeople.linkCopied')} -
- - - ); - } - - const displayUrl = getDecodedURI(url); - - return ( - <> -
- {isHovered ? t('addPeople.copyLink') : displayUrl} -
- - - ); - } - return ( <> {t('addPeople.shareLink')} -
- { renderLinkContent() } -
+ ); } From 9e7a477797c6446432b8905c7d3035dc04b85c2b Mon Sep 17 00:00:00 2001 From: Tristian Flanagan Date: Wed, 22 Jul 2020 10:37:17 -0400 Subject: [PATCH 101/167] feat(blur): terminate blur web worker when disabled (#7347) * feat(blur): terminate blur web worker when disabled --- .../features/stream-effects/blur/JitsiStreamBlurEffect.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/react/features/stream-effects/blur/JitsiStreamBlurEffect.js b/react/features/stream-effects/blur/JitsiStreamBlurEffect.js index e432a3f81..011bfe71e 100644 --- a/react/features/stream-effects/blur/JitsiStreamBlurEffect.js +++ b/react/features/stream-effects/blur/JitsiStreamBlurEffect.js @@ -43,9 +43,6 @@ export default class JitsiStreamBlurEffect { this._outputCanvasElement = document.createElement('canvas'); this._outputCanvasElement.getContext('2d'); this._inputVideoElement = document.createElement('video'); - - this._maskFrameTimerWorker = new Worker(timerWorkerScript, { name: 'Blur effect worker' }); - this._maskFrameTimerWorker.onmessage = this._onMaskFrameTimer; } /** @@ -104,6 +101,9 @@ export default class JitsiStreamBlurEffect { * @returns {MediaStream} - The stream with the applied effect. */ startEffect(stream: MediaStream) { + this._maskFrameTimerWorker = new Worker(timerWorkerScript, { name: 'Blur effect worker' }); + this._maskFrameTimerWorker.onmessage = this._onMaskFrameTimer; + const firstVideoTrack = stream.getVideoTracks()[0]; const { height, frameRate, width } = firstVideoTrack.getSettings ? firstVideoTrack.getSettings() : firstVideoTrack.getConstraints(); @@ -133,5 +133,7 @@ export default class JitsiStreamBlurEffect { this._maskFrameTimerWorker.postMessage({ id: CLEAR_INTERVAL }); + + this._maskFrameTimerWorker.terminate(); } } From a23dac2ab6191e007efe5c8253ab7e4a7bcffa1b Mon Sep 17 00:00:00 2001 From: Kevin Olbrich Date: Wed, 22 Jul 2020 18:49:09 +0200 Subject: [PATCH 102/167] Add remark about focus username https://github.com/jitsi/jitsi-meet/issues/7376 --- config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.js b/config.js index 34d2fb05d..a54240e6b 100644 --- a/config.js +++ b/config.js @@ -37,6 +37,8 @@ var config = { clientNode: 'http://jitsi.org/jitsimeet', // The real JID of focus participant - can be overridden here + // Do not change username - FIXME: Make focus username configurable + // https://github.com/jitsi/jitsi-meet/issues/7376 // focusUserJid: 'focus@auth.jitsi-meet.example.com', From 28094947a772440d34f9ec0fabb624b5f3d336b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Orman?= Date: Thu, 23 Jul 2020 11:39:48 +0200 Subject: [PATCH 103/167] lang: updated Polish translations --- lang/main-pl.json | 177 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 156 insertions(+), 21 deletions(-) diff --git a/lang/main-pl.json b/lang/main-pl.json index 0994de687..8f213ef8c 100644 --- a/lang/main-pl.json +++ b/lang/main-pl.json @@ -1,21 +1,36 @@ { "addPeople": { "add": "Zaproś", + "addContacts": "Zaproś kontakty", + "copyInvite": "Skopiuj zaproszenie", + "copyLink": "Skopiuj odnośnik do spotkania", + "copyStream": "Skopiuj odnośnik do transmisji", "countryNotSupported": "Nie obsługujemy jeszcze tej lokalizacji.", "countryReminder": "Dzwonisz spoza USA? Upewnij się, że zaczynasz od kodu kraju!", + "defaultEmail": "Domyślny klient email", "disabled": "Nie możesz zapraszać ludzi.", "failedToAdd": "Błąd dodawania uczestników", "footerText": "Wybieranie numeru jest wyłączone.", + "googleEmail": "Google", + "inviteMoreHeader": "Jesteś jedynym uczestnikiem spotkania", + "inviteMoreMailSubject": "Dołącz do spotkania w {{appName}}", + "inviteMorePrompt": "Zaproś kolejne osoby", + "linkCopied": "Skopiowano odnośnik do schowka", "loading": "Szukaj ludzi i numerów telefonów", "loadingNumber": "Weryfikacja numeru telefonu", "loadingPeople": "Wyszukiwanie osób do zaproszenia", "noResults": "Brak pasujących wyników wyszukiwania", "noValidNumbers": "Proszę wpisać numer telefonu", + "outlookEmail": "Outlook", "searchNumbers": "Dodaj numery telefonów", "searchPeople": "Szukaj ludzi", "searchPeopleAndNumbers": "Wyszukaj osoby i dodaj ich numery telefonu", + "shareInvite": "Udostępnij zaproszenie", + "shareLink": "Udostępnij odnośnik do spotkania", + "shareStream": "Udostępnij odnośnik do transmisji na żywo", "telephone": "Telefon: {{number}}", - "title": "Zaproś ludzi na to spotkanie" + "title": "Zaproś inne osoby do tego spotkania", + "yahooEmail": "Yahoo" }, "audioDevices": { "bluetooth": "Bluetooth", @@ -72,7 +87,11 @@ "DISCONNECTED": "Rozłączony", "DISCONNECTING": "Rozłączanie", "ERROR": "Błąd", - "RECONNECTING": "Wystąpił problem w sieci. Ponowienie połączenia..." + "RECONNECTING": "Wystąpił problem sieciowy. Ponowienie połączenia...", + "FETCH_SESSION_ID": "Pobieranie identyfikators sesji...", + "GET_SESSION_ID_ERROR": "Błąd pobierania identyfikatora sesji: {{code}}", + "GOT_SESSION_ID": "Pobieranie identyfikators sesji... ok", + "LOW_BANDWIDTH": "Wideo dla {{displayName}} zostało wyłączone z powodu ograniczonej przepustowości" }, "connectionindicator": { "address": "Adres:", @@ -125,7 +144,7 @@ "tryAgainButton": "Spróbuj ponownie w aplikacji stacjonarnej" }, "defaultLink": "np. {{url}}", - "defaultNickname": "np. Ziutek Kowalski", + "defaultNickname": "np. Jan Kowalski", "deviceError": { "cameraError": "Błąd dostępu do Twojej kamery", "cameraPermission": "Błąd podczas otrzymywania uprawnień do kamery", @@ -171,6 +190,12 @@ "dismiss": "Odrzuć", "displayNameRequired": "Cześć! Jak się nazywasz?", "done": "Zrobione", + "e2eeDescription": "Wsparcie dla szyfrowania End-to-End jest obecnie EKSPERYMENTALNE. Włączenie opcji szyfrowania end-to-end uniemożliwi korzystanie z takich usług jak: nagrywanie, transmisja na żywo czy połączenia telefoniczne. Spotkanie będzie działać prawidłowo jedynie dla użytkowników posiadających przeglądarki ze wsparciem dla tego rodzaju szyfrowania.", + "e2eeLabel": "Klucz E2EE", + "e2eeNoKey": "Brak klucza", + "e2eeToggleSet": "Ustaw klucz", + "e2eeSet": "Ustaw", + "e2eeWarning": "UWAGA: Nie wszyscy uczestnicy tego spotkania posiadają wsparcie dla szyfrowania End-to-End. Po włączeniu tej opcji nie będa oni w stanie Cię widzieć ani słyszeć.", "enterDisplayName": "Wpisz tutaj swoje imię", "error": "Błąd", "externalInstallationMsg": "Zainstaluj rozszerzenie naszego współdzielenia ekranu.", @@ -279,6 +304,9 @@ "documentSharing": { "title": "Współdzielony dokument" }, + "e2ee": { + "labelToolTip": "Komunikacja audio i wideo dla tej rozmowy jest szyfrowana end-to-end" + }, "feedback": { "average": "Średnio", "bad": "Źle", @@ -447,11 +475,51 @@ "unmute": "Wyłącz wyciszenie", "newDeviceCameraTitle": "Wykryto nową kamerę", "newDeviceAudioTitle": "Wykryto nowe urządzenie dźwiękowe", - "newDeviceAction": "Użyj" + "newDeviceAction": "Użyj", + "OldElectronAPPTitle": "Zagrożenie bezpieczeństwa!", + "oldElectronClientDescription1": "Używasz starej wersji klienta Jitsi Meet, który posiada znane problemy bezpieczeństwa. Zaktualizuj klienta do naszej ", + "oldElectronClientDescription2": "najnowszej wersji", + "oldElectronClientDescription3": " teraz!" }, "passwordSetRemotely": "wybrane przez innego uczestnika", "passwordDigitsOnly": "", "poweredby": "napędzane dzięki", + "prejoin": { + "audioAndVideoError": "Błąd audio i wideo:", + "audioOnlyError": "Błąd audio:", + "audioTrackError": "Nie można utworzyć ścieżki audio.", + "calling": "Dzwonię", + "callMe": "Zadzwoń do mnie", + "callMeAtNumber": "Zadzwoń do mnie pod tym numerem:", + "configuringDevices": "Konfigurowanie urządzeń...", + "connectedWithAudioQ": "Jesteś podłączony z audio?", + "copyAndShare": "Skopiuj i udostępnij link spotkania", + "dialInMeeting": "Wdzwoń się do spotkania", + "dialInPin": "Wdzwoń się do spotkania i podej PIN:", + "dialing": "Wdzwanianie", + "doNotShow": "Nie pokazuj ponownie", + "errorDialOut": "Nie można wybrać numeru", + "errorDialOutDisconnected": "Nie można wybrać numeru. Rozłączono", + "errorDialOutFailed": "Nie można wybrać numeru. Błąd połączenia", + "errorDialOutStatus": "Błąd pobierania statusu połączenia", + "errorStatusCode": "Błąd połączenia, status: {{status}}", + "errorValidation": "Numer jest nieprawidłowy", + "iWantToDialIn": "Chce się wdzwonić", + "joinAudioByPhone": "Dołącz z audio telefonu", + "joinMeeting": "Dołącz do spotkania", + "joinWithoutAudio": "Dołącz bez audio", + "initiated": "Rozmowa rozpoczęta", + "linkCopied": "Skopiowano link do schowka", + "lookGood": "Wygląda na to, że Twój mikrofon działa poprawnie", + "or": "albo", + "premeeting": "Przed spotkaniem", + "showScreen": "Włącz konfigurację przed spotkaniem", + "startWithPhone": "Rozpocznij z audio telefonu", + "screenSharingError": "Błąd współdzielenia ekranu:", + "videoOnlyError": "Błąd wideo:", + "videoTrackError": "Nie można utworzyć ścieżki wideo.", + "viewAllNumbers": "pokaż wszystkie numery" + }, "presenceStatus": { "busy": "Zajęte", "calling": "Dzwonienie…", @@ -474,6 +542,8 @@ }, "raisedHand": "Chcesz się odezwać ?", "recording": { + "limitNotificationDescriptionWeb": "Ze względu na duże zapotrzebowanie twoje nagranie zostanie ograniczone do {{limit}} min. Dla nieograniczonych nagrań spróbuj {{app}}.", + "limitNotificationDescriptionNative": "Ze względu na duże zapotrzebowanie twoje nagranie zostanie ograniczone do {{limit}} min. Dla nieograniczonych nagrań spróbuj <3>{{app}}.", "authDropboxText": "Prześlij na Dropbox", "availableSpace": "", "beta": "BETA", @@ -503,6 +573,12 @@ "sectionList": { "pullToRefresh": "Przeciągnij, aby odświeżyć" }, + "security": { + "about": "Możesz ustawić $t(lockRoomPassword) do spotkania. Uczestnicy będą musieli podać $t(lockRoomPassword), aby do niego dołączyć.", + "aboutReadOnly": "Moderatorzy mogą ustawić $t(lockRoomPassword) do spotkania. Uczestnicy będą musieli podać $t(lockRoomPassword), aby do niego dołączyć.", + "insecureRoomNameWarning": "Pokój nie jest zabezpieczony. Niechciani uczestnicy mogą dołączyć do spotkania. Rozważ zabezpieczenie swojego pokoju w opcjach bezpieczeństwa.", + "securityOptions": "Opcje bezpieczeństwa" + }, "settings": { "calendar": { "about": "", @@ -533,14 +609,16 @@ "alertURLText": "Wprowadzony adres URL serwera jest nieprawidłowy", "buildInfoSection": "Informacja o kompilacji", "conferenceSection": "Konferencja", - "disableCallIntegration": "", - "disableP2P": "", + "disableCallIntegration": "Wyłącz natywne rozmowy", + "disableP2P": "Wyłącz tryb Peer-to-Peer", + "disableCrashReporting": "Wyłącz raportowanie awarii", + "disableCrashReportingWarning": "Jesteś pewien, że chcesz wyłączyć raportowanie awarii? Ta zmiana zostanie zastosowana po zrestartowaniu aplikacji.", "displayName": "Wyświetlana nazwa", "email": "E-mail", "header": "Ustawienia", "profileSection": "Profil", "serverURL": "Adres URL serwera", - "showAdvanced": "", + "showAdvanced": "Zaawansowane ustawienia", "startWithAudioMuted": "Rozpocznij z wyciszonym dźwiękiem", "startWithVideoMuted": "Rozpocznij z wyłączonym obrazem", "version": "Wersja" @@ -560,7 +638,7 @@ }, "startupoverlay": { "policyText": " ", - "title": "{{app}} potrzebuje używać Twój mikrofon i kamerę." + "title": "{{app}} potrzebuje używać Twojego mikrofonu i kamery." }, "suspendedoverlay": { "rejoinKeyTitle": "Dołącz ponownie", @@ -576,34 +654,40 @@ "chat": "Przełączanie okna rozmowy", "document": "Przełączanie wspólnego dokumentu", "download": "Pobierz nasze aplikacje", + "e2ee": "Szyfrowanie End-to-End", "feedback": "Zostaw swoją opinię", "fullScreen": "Przełączanie trybu pełnoekranowego", - "hangup": "Zostaw rozmowę", + "grantModerator": "Przydziel uprawnienia moderatora", + "hangup": "Opuść rozmowę", "help": "Pomoc", "invite": "Zapraszaj ludzi", "kick": "Usuń uczestnika", + "lobbyButton": "Włącz/wyłącz lobby", "localRecording": "Przełączanie lokalnych urządzeń sterujących zapisem danych", "lockRoom": "Przełączenie hasła spotkania", "moreActions": "Przełączanie menu więcej działań", "moreActionsMenu": "Więcej działań w menu", + "moreOptions": "Pokaż więcej opcji", "mute": "Uruchamianie wyciszonego audycji", + "muteEveryone": "Wycisz wszystkich", "pip": "Tryb przełączania obrazu-w-obrazie", "privateMessage": "Wyślij wiadomość prywatną", "profile": "Edytuj swój profil", - "raiseHand": "Przełączyć rękę w górę", + "raiseHand": "Podnieś / Opuść rękę", "recording": "Przełączanie nagrywania", "remoteMute": "Wycisz uczestnika", - "Settings": "Ustawienia przełączania", + "security": "Opcje bezpieczeństwa", + "Settings": "Ustawienia", "sharedvideo": "Przełącz udostępnianie obrazu na YouTube", "shareRoom": "Zaproś kogoś", - "shareYourScreen": "Przełączanie podziału ekranu", - "shortcuts": "Przełączanie skrótów klawiszowych", + "shareYourScreen": "Współdziel ekran", + "shortcuts": "Wyświetl skróty", "show": "", - "speakerStats": "Przełączanie statystyk dotyczących mówców", - "tileView": "Przełącz widok kafelkowy", + "speakerStats": "Statystyki mówców", + "tileView": "Widok kafelkowy", "toggleCamera": "Przełączanie kamery", - "videomute": "Przełączanie wyciszonego filmu wideo", - "videoblur": "Przełącz rozmazanie obrazu" + "videomute": "Włącz / Wyłącz kamerę", + "videoblur": "Włącz / Wyłącz rozmazanie obrazu" }, "addPeople": "Dodaj ludzi do swojego telefonu", "audioOnlyOff": "Wyłącz tryb słabego łącza", @@ -616,6 +700,7 @@ "documentClose": "Zamknij wspólny dokument", "documentOpen": "Otwarty współdzielony dokument", "download": "Pobierz nasze aplikacje", + "e2ee": "Szyfrowanie End-to-End", "enterFullScreen": "Wyświetlanie pełnego ekranu", "enterTileView": "Wejdź w kafelkowy widok", "exitFullScreen": "Wyświetlanie pełnego ekranu", @@ -624,20 +709,29 @@ "hangup": "Opuść", "help": "Pomoc", "invite": "Zapraszaj ludzi", + "lobbyButtonDisable": "Wyłącz lobby", + "lobbyButtonEnable": "Włącz lobby", "login": "Zaloguj", "logout": "Wyloguj", "lowerYourHand": "Opuść rękę", "moreActions": "Więcej działań", + "moreOptions": "Więcej opcji", "mute": "Włącz / Wyłącz mikrofon", - "noAudioSignalTitle": "", - "noAudioSignalDesc": "", - "noAudioSignalDescSuggestion": "", + "muteEveryone": "Wycisz wszystkich", + "noAudioSignalTitle": "Brak sygnału audio!", + "noAudioSignalDesc": "Jeżeli celowo nie wyciszyłeś mikrofonu w ustawieniach systemowych spróbuj innego urządzenia.", + "noAudioSignalDescSuggestion": "Jeżeli celowo nie wyciszyłeś mikrofonu w ustawieniach systemowych spróbuj sugerowanego urządzenia.", + "noAudioSignalDialInDesc": "Wdzwoń się używając:", + "noAudioSignalDialInLinkDesc": "Numery", + "noisyAudioInputTitle": "Twój mikrofon wydaje się być hałaśliwy!", + "noisyAudioInputDesc": "Twój mikrofon wydaje się hałaśliwy, wycisz mikrofon albo zmień urządzenie.", "openChat": "Otwórz rozmowę", "pip": "Wprowadź tryb obrazu w obrazie", "privateMessage": "Wyślij wiadomość prywatną", "profile": "Edytuj swój profil", "raiseHand": "Podnieś / Opuść rękę", "raiseYourHand": "Podnieś rękę", + "security": "Opcje bezpieczeństwa", "Settings": "Ustawienia", "sharedvideo": "Udostępnij wideo z Youtube", "shareRoom": "Zaproś kogoś", @@ -732,10 +826,11 @@ "connectCalendarButton": "Podłącz swój kalendarz", "connectCalendarText": "", "enterRoomTitle": "Rozpocznij nowe spotkanie", + "getHelp": "Pomoc", "roomNameAllowedChars": "Nazwa spotkania nie powinna zawierać żadnego z tych znaków: ?, &, :, ', \", %, #.", "go": "IDŹ", "goSmall": "IDŹ", - "join": "", + "join": "Stwórz / Dołącz", "info": "Informacje", "privacy": "Polityka prywatności", "recentList": "Niedawno", @@ -747,5 +842,45 @@ "sendFeedback": "Wyślij opinię", "terms": "Warunki korzystania", "title": "Bezpieczna, w pełni funkcjonalna i całkowicie bezpłatna wideokonferencja" + }, + "lonelyMeetingExperience": { + "button": "Zaproś", + "youAreAlone": "Jesteś jedyną osobą na spotkaniu" + }, + "helpView": { + "header": "Pomoc" + }, + "lobby": { + "knockingParticipantList": "Oczekujący uczestnicy", + "allow": "Zezwól", + "backToKnockModeButton": "Brak hasła, poproś o dołączenie", + "dialogTitle": "Lobby", + "disableDialogContent": "Lobby jest aktualnie włączone. Ta funkcjonalność zapewnia, że niechciani uczetnicy nie mogą dołączyć do spotkania. Czy chcesz wyłączyć tę opcję?", + "disableDialogSubmit": "Wyłącz", + "emailField": "Podaj adres email", + "enableDialogPasswordField": "Ustaw hasło (opcjonalne)", + "enableDialogSubmit": "Włącz", + "enableDialogText": "Lobby umożliwia zabezpieczenie spotkania przed dostępem niechcianych osób. Uczestnik może dołączyć do spotkania tylko po zaakceptowaniu przez moderatora.", + "enterPasswordButton": "Hasło spotkania", + "enterPasswordTitle": "Wprowadź hasło aby dołączyć", + "invalidPassword": "Nieprawidłowe hasło", + "joiningMessage": "Dołączysz do spotkania po zaakceptowaniu Twojej prośby", + "joinWithPasswordMessage": "Dołączanie z hasłem, proszę czekać...", + "joinRejectedMessage": "Twoja prośba została odrzucona przez moderatora.", + "joinTitle": "Dołącz do spotkania", + "joiningTitle": "Dołączanie do spotkania...", + "joiningWithPasswordTitle": "Dołączanie z hasłem...", + "knockButton": "Poproś o dołączenie", + "knockTitle": "Ktoś chce dołączyć do spotkania", + "nameField": "Podaj swoje imię", + "notificationLobbyAccessDenied": "{{targetParticipantName}} został odrzucony przez {{originParticipantName}}", + "notificationLobbyAccessGranted": "{{targetParticipantName}} został zaakceptowany przez {{originParticipantName}}", + "notificationLobbyDisabled": "Lobby zostało wyłączone przez {{originParticipantName}}", + "notificationLobbyEnabled": "Lobby zostało włączone przez {{originParticipantName}}", + "notificationTitle": "Lobby", + "passwordField": "Wprowadź hasło", + "passwordJoinButton": "Dołącz", + "reject": "Odrzuć", + "toggleLabel": "Włącz / Wyłącz lobby" } } From b1e12d33abbd9d5f065080be496662890e52e38c Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Thu, 23 Jul 2020 11:16:40 +0300 Subject: [PATCH 104/167] feat(embed) implement embed meeting feature --- css/buttons/copy.scss | 1 + css/main.scss | 1 + css/modals/embed-meeting/_embed-meeting.scss | 59 ++++++++++++++++ css/modals/invite/_invite_more.scss | 4 -- interface_config.js | 2 +- lang/main.json | 6 ++ react/features/base/icons/svg/code-block.svg | 4 ++ react/features/base/icons/svg/index.js | 1 + .../components/EmbedMeetingDialog.js | 69 +++++++++++++++++++ .../components/EmbedMeetingTrigger.js | 50 ++++++++++++++ .../embed-meeting/components/Header.js | 38 ++++++++++ .../embed-meeting/components/index.js | 1 + react/features/embed-meeting/index.js | 1 + .../add-people-dialog/web/AddPeopleDialog.js | 3 + .../web/InviteByEmailSection.js | 1 - .../toolbox/components/web/Toolbox.js | 35 ++++++++++ 16 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 css/modals/embed-meeting/_embed-meeting.scss create mode 100644 react/features/base/icons/svg/code-block.svg create mode 100644 react/features/embed-meeting/components/EmbedMeetingDialog.js create mode 100644 react/features/embed-meeting/components/EmbedMeetingTrigger.js create mode 100644 react/features/embed-meeting/components/Header.js create mode 100644 react/features/embed-meeting/components/index.js create mode 100644 react/features/embed-meeting/index.js diff --git a/css/buttons/copy.scss b/css/buttons/copy.scss index 9596abcc3..20867ce51 100644 --- a/css/buttons/copy.scss +++ b/css/buttons/copy.scss @@ -21,6 +21,7 @@ text-overflow: ellipsis; white-space: nowrap; max-width: 292px; + margin-right: 16px; &.selected { font-weight: 600; diff --git a/css/main.scss b/css/main.scss index 1257937f2..4b3ed8c4e 100644 --- a/css/main.scss +++ b/css/main.scss @@ -37,6 +37,7 @@ $flagsImagePath: "../images/"; @import 'modals/desktop-picker/desktop-picker'; @import 'modals/device-selection/device-selection'; @import 'modals/dialog'; +@import 'modals/embed-meeting/embed-meeting'; @import 'modals/feedback/feedback'; @import 'modals/invite/info'; @import 'modals/settings/settings'; diff --git a/css/modals/embed-meeting/_embed-meeting.scss b/css/modals/embed-meeting/_embed-meeting.scss new file mode 100644 index 000000000..3d3a7f21a --- /dev/null +++ b/css/modals/embed-meeting/_embed-meeting.scss @@ -0,0 +1,59 @@ +.embed-meeting { + &-dialog { + display: flex; + flex-direction: column; + + &-header { + display: flex; + justify-content: space-between; + margin: 16px 16px 24px; + width: calc(100% - 32px); + color: #fff; + font-weight: 600; + font-size: 24px; + line-height: 32px; + + & > div > svg { + cursor: pointer; + fill: #A4B8D1; + } + } + } + + &-copy { + color: white; + font-size: 15px; + margin-left: auto; + margin-top: 16px; + width: auto; + } + + &-code { + background: transparent; + border: 1px solid #A4B8D1; + color: white; + font-size: 15px; + height: 165px; + line-height: 24px; + padding: 8px; + width: 100%; + resize: vertical; + } + + &-trigger { + display: flex; + align-items: center; + padding: 8px 8px 8px 16px; + margin-top: 24px; + width: calc(100% - 24px); + height: 24px; + background: #2A3A4B; + border: 1px solid #5E6D7A; + border-radius: 4px; + cursor: pointer; + + .jitsi-icon { + margin-right: 20px; + } + } +} diff --git a/css/modals/invite/_invite_more.scss b/css/modals/invite/_invite_more.scss index 73160928a..742f4e898 100644 --- a/css/modals/invite/_invite_more.scss +++ b/css/modals/invite/_invite_more.scss @@ -47,10 +47,6 @@ font-size: 15px; line-height: 24px; - & > span { - font-weight: 600; - } - &.header { display: flex; justify-content: space-between; diff --git a/interface_config.js b/interface_config.js index dfcc6cdfc..32f8f221a 100644 --- a/interface_config.js +++ b/interface_config.js @@ -185,7 +185,7 @@ var interfaceConfig = { * - 'desktop' controls the "Share your screen" button */ TOOLBAR_BUTTONS: [ - 'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen', + 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen', 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording', 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand', 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts', diff --git a/lang/main.json b/lang/main.json index bd326a158..b98ec138a 100644 --- a/lang/main.json +++ b/lang/main.json @@ -190,6 +190,7 @@ "connectErrorWithMsg": "Oops! Something went wrong and we couldn't connect to the conference: {{msg}}", "connecting": "Connecting", "contactSupport": "Contact support", + "copied": "Copied", "copy": "Copy", "dismiss": "Dismiss", "displayNameRequired": "Hi! What’s your name?", @@ -316,6 +317,9 @@ "e2ee": { "labelToolTip": "Audio and Video Communication on this call is end-to-end encrypted" }, + "embedMeeting": { + "title": "Embed this meeting" + }, "feedback": { "average": "Average", "bad": "Bad", @@ -670,6 +674,7 @@ "chat": "Toggle chat window", "document": "Toggle shared document", "download": "Download our apps", + "embedMeeting": "Embed meeting", "e2ee": "End-to-End Encryption", "feedback": "Leave feedback", "fullScreen": "Toggle full screen", @@ -718,6 +723,7 @@ "documentOpen": "Open shared document", "download": "Download our apps", "e2ee": "End-to-End Encryption", + "embedMeeting": "Embed meeting", "enterFullScreen": "View full screen", "enterTileView": "Enter tile view", "exitFullScreen": "Exit full screen", diff --git a/react/features/base/icons/svg/code-block.svg b/react/features/base/icons/svg/code-block.svg new file mode 100644 index 000000000..5c5783b2d --- /dev/null +++ b/react/features/base/icons/svg/code-block.svg @@ -0,0 +1,4 @@ + + + + diff --git a/react/features/base/icons/svg/index.js b/react/features/base/icons/svg/index.js index b9f38b58f..e7f43a451 100644 --- a/react/features/base/icons/svg/index.js +++ b/react/features/base/icons/svg/index.js @@ -20,6 +20,7 @@ export { default as IconCheck } from './check.svg'; export { default as IconClose } from './close.svg'; export { default as IconCloseX } from './close-x.svg'; export { default as IconClosedCaption } from './closed_caption.svg'; +export { default as IconCodeBlock } from './code-block.svg'; export { default as IconConnectionActive } from './gsm-bars.svg'; export { default as IconConnectionInactive } from './ninja.svg'; export { default as IconCopy } from './copy.svg'; diff --git a/react/features/embed-meeting/components/EmbedMeetingDialog.js b/react/features/embed-meeting/components/EmbedMeetingDialog.js new file mode 100644 index 000000000..5da5a2caa --- /dev/null +++ b/react/features/embed-meeting/components/EmbedMeetingDialog.js @@ -0,0 +1,69 @@ +// @flow + +import React from 'react'; +import { connect } from 'react-redux'; + +import CopyButton from '../../base/buttons/CopyButton'; +import { getInviteURL } from '../../base/connection'; +import { Dialog } from '../../base/dialog'; +import { translate } from '../../base/i18n'; + +import Header from './Header'; + +type Props = { + + /** + * Invoked to obtain translated strings. + */ + t: Function, + + /** + * The URL of the conference. + */ + url: string +}; + +/** + * Allow users to embed a jitsi meeting in an iframe. + * + * @returns {React$Element} + */ +function EmbedMeeting({ t, url }: Props) { + /** + * Get the embed code for a jitsi meeting. + * + * @returns {string} The iframe embed code. + */ + const getEmbedCode = () => + `'; + + return ( + +
+